116
116
Object.extend(Squeak,
117
117
"version", {
118
118
// system attributes
119
- vmVersion: "SqueakJS 1.2.1 ",
120
- vmDate: "2024-05-27 ", // Maybe replace at build time?
119
+ vmVersion: "SqueakJS 1.2.2 ",
120
+ vmDate: "2024-06-22 ", // Maybe replace at build time?
121
121
vmBuild: "unknown", // or replace at runtime by last-modified?
122
122
vmPath: "unknown", // Replace at runtime
123
123
vmFile: "vm.js",
4912
4912
},
4913
4913
stObjectatput: function(array, index, obj) {
4914
4914
if (array.sqClass !== this.classArray()) throw Error("Array expected");
4915
- if (index < 1 || index >= array.pointers.length) return this.successFlag = false;
4916
- array.pointers[index] = obj;
4915
+ if (index < 1 || index > array.pointers.length) return this.successFlag = false;
4916
+ array.pointers[index-1 ] = obj;
4917
4917
},
4918
4918
},
4919
4919
'constant access',
@@ -10071,14 +10071,39 @@
10071
10071
Object.extend(Squeak.Primitives.prototype,
10072
10072
'input', {
10073
10073
primitiveClipboardText: function(argCount) {
10074
+ // There are two ways this primitive is invoked:
10075
+ // 1: via the DOM keyboard event thandler in squeak.js that intercepts cmd-c/cmd-v,
10076
+ // reads/writes the system clipboard from/to display.clipboardString
10077
+ // and then the interpreter calls the primitive
10078
+ // 2: via the image code e.g. a menu copy/paste item, which calls the primitive
10079
+ // and we try to read/write the system clipboard directly.
10080
+ // To support this, squeak.js keeps running the interpreter for 100 ms within
10081
+ // the DOM event 'mouseup' handler so the code below runs in the click-handler context,
10082
+ // (otherwise the browser would block access to the clipboard)
10074
10083
if (argCount === 0) { // read from clipboard
10075
- if (typeof(this.display.clipboardString) !== 'string') return false;
10076
- this.vm.popNandPush(1, this.makeStString(this.display.clipboardString));
10084
+ // Try to read from system clipboard, which is async if available.
10085
+ // It will likely fail outside of an event handler.
10086
+ var clipBoardPromise = null;
10087
+ if (this.display.readFromSystemClipboard) clipBoardPromise = this.display.readFromSystemClipboard();
10088
+ if (clipBoardPromise) {
10089
+ var unfreeze = this.vm.freeze();
10090
+ clipBoardPromise
10091
+ .then(() => this.vm.popNandPush(1, this.makeStString(this.display.clipboardString)))
10092
+ .catch(() => this.vm.popNandPush(1, this.vm.nilObj))
10093
+ .finally(() => unfreeze());
10094
+ } else {
10095
+ if (typeof(this.display.clipboardString) !== 'string') return false;
10096
+ this.vm.popNandPush(1, this.makeStString(this.display.clipboardString));
10097
+ }
10077
10098
} else if (argCount === 1) { // write to clipboard
10078
10099
var stringObj = this.vm.top();
10079
10100
if (stringObj.bytes) {
10080
10101
this.display.clipboardString = stringObj.bytesAsString();
10081
- this.display.clipboardStringChanged = true;
10102
+ this.display.clipboardStringChanged = true; // means it should be written to system clipboard
10103
+ if (this.display.writeToSystemClipboard) {
10104
+ // no need to wait for the promise
10105
+ this.display.writeToSystemClipboard();
10106
+ }
10082
10107
}
10083
10108
this.vm.pop();
10084
10109
}
56270
56295
display.signalInputEvent();
56271
56296
}
56272
56297
display.idle = 0;
56273
- if (what == 'mouseup') {
56274
- if (display.runFor) display.runFor(100); // maybe we catch the fullscreen flag change
56275
- } else {
56276
- if (display.runNow) display.runNow(); // don't wait for timeout to run
56277
- }
56298
+ if (what === 'mouseup') display.runFor(100, what); // process copy/paste or fullscreen flag change
56299
+ else display.runNow(what); // don't wait for timeout to run
56278
56300
}
56279
56301
56280
56302
function recordWheelEvent(evt, display) {
56297
56319
if (display.signalInputEvent)
56298
56320
display.signalInputEvent();
56299
56321
display.idle = 0;
56322
+ if (display.runNow) display.runNow('wheel'); // don't wait for timeout to run
56300
56323
}
56301
56324
56302
56325
// Squeak traditional keycodes are MacRoman
56348
56371
display.keys.push(modifiersAndKey);
56349
56372
}
56350
56373
display.idle = 0;
56351
- if (display.runNow) display.runNow(); // don't wait for timeout to run
56374
+ if (display.runNow) display.runNow('keyboard' ); // don't wait for timeout to run
56352
56375
}
56353
56376
56354
56377
function recordDragDropEvent(type, evt, canvas, display) {
56365
56388
]);
56366
56389
if (display.signalInputEvent)
56367
56390
display.signalInputEvent();
56391
+ display.idle = 0;
56392
+ if (display.runNow) display.runNow('drag-drop'); // don't wait for timeout to run
56368
56393
}
56369
56394
56370
56395
function fakeCmdOrCtrlKey(key, timestamp, display) {
56404
56429
eventQueue: null, // only used if image uses event primitives
56405
56430
clipboardString: '',
56406
56431
clipboardStringChanged: false,
56432
+ handlingEvent: '', // set to 'mouse' or 'keyboard' while handling an event
56407
56433
cursorCanvas: options.cursor !== false && document.getElementById("sqCursor") || document.createElement("canvas"),
56408
56434
cursorOffsetX: 0,
56409
56435
cursorOffsetY: 0,
56490
56516
ctx.fillStyle = style.color || "#F90";
56491
56517
ctx.fillRect(x, y, w * value, h);
56492
56518
};
56493
- display.executeClipboardPaste = function(text, timestamp) {
56519
+ display.executeClipboardPasteKey = function(text, timestamp) {
56494
56520
if (!display.vm) return true;
56495
56521
try {
56496
56522
display.clipboardString = text;
56500
56526
console.error("paste error " + err);
56501
56527
}
56502
56528
};
56503
- display.executeClipboardCopy = function(key, timestamp) {
56529
+ display.executeClipboardCopyKey = function(key, timestamp) {
56504
56530
if (!display.vm) return true;
56505
56531
// simulate copy event for Squeak so it places its text in clipboard
56506
56532
display.clipboardStringChanged = false;
@@ -56906,18 +56932,18 @@
56906
56932
switch (evt.key) {
56907
56933
case "c":
56908
56934
case "x":
56909
- if (!navigator.clipboard?.writeText ) return; // fire document.oncopy/oncut
56910
- var text = display.executeClipboardCopy (evt.key, evt.timeStamp);
56935
+ if (!navigator.clipboard) return; // fire document.oncopy/oncut
56936
+ var text = display.executeClipboardCopyKey (evt.key, evt.timeStamp);
56911
56937
if (typeof text === 'string') {
56912
56938
navigator.clipboard.writeText(text)
56913
56939
.catch(function(err) { console.error("display: copy error " + err.message); });
56914
56940
}
56915
56941
return evt.preventDefault();
56916
56942
case "v":
56917
- if (!navigator.clipboard?.readText ) return; // fire document.onpaste
56943
+ if (!navigator.clipboard) return; // fire document.onpaste
56918
56944
navigator.clipboard.readText()
56919
56945
.then(function(text) {
56920
- display.executeClipboardPaste (text, evt.timeStamp);
56946
+ display.executeClipboardPasteKey (text, evt.timeStamp);
56921
56947
})
56922
56948
.catch(function(err) { console.error("display: paste error " + err.message); });
56923
56949
return evt.preventDefault();
@@ -56935,10 +56961,25 @@
56935
56961
if (!display.vm) return true;
56936
56962
recordModifiers(evt, display);
56937
56963
};
56938
- // copy/paste old-style
56939
- if (!navigator.clipboard?.writeText) {
56964
+ // more copy/paste
56965
+ if (navigator.clipboard) {
56966
+ // new-style copy/paste (all modern browsers)
56967
+ display.readFromSystemClipboard = () => navigator.clipboard.readText()
56968
+ .then(text => display.clipboardString = text)
56969
+ .catch(err => {
56970
+ if (!display.handlingEvent) console.warn("reading from clipboard outside event handler");
56971
+ console.error("readFromSystemClipboard" + err.message);
56972
+ });
56973
+ display.writeToSystemClipboard = () => navigator.clipboard.writeText(display.clipboardString)
56974
+ .then(() => display.clipboardStringChanged = false)
56975
+ .catch(err => {
56976
+ if (!display.handlingEvent) console.warn("writing to clipboard outside event handler");
56977
+ console.error("writeToSystemClipboard" + err.message);
56978
+ });
56979
+ } else {
56980
+ // old-style copy/paste
56940
56981
document.oncopy = function(evt, key) {
56941
- var text = display.executeClipboardCopy (key, evt.timeStamp);
56982
+ var text = display.executeClipboardCopyKey (key, evt.timeStamp);
56942
56983
if (typeof text === 'string') {
56943
56984
evt.clipboardData.setData("Text", text);
56944
56985
}
56948
56989
if (!display.vm) return true;
56949
56990
document.oncopy(evt, 'x');
56950
56991
};
56951
- }
56952
- if (!navigator.clipboard?.readText) {
56953
56992
document.onpaste = function(evt) {
56954
56993
var text = evt.clipboardData.getData('Text');
56955
- display.executeClipboardPaste (text, evt.timeStamp);
56994
+ display.executeClipboardPasteKey (text, evt.timeStamp);
56956
56995
evt.preventDefault();
56957
56996
};
56958
56997
}
@@ -57155,15 +57194,17 @@
57155
57194
alert(error);
57156
57195
}
57157
57196
}
57158
- display.runNow = function() {
57197
+ display.runNow = function(event ) {
57159
57198
window.clearTimeout(loop);
57199
+ display.handlingEvent = event;
57160
57200
run();
57201
+ display.handlingEvent = '';
57161
57202
};
57162
- display.runFor = function(milliseconds) {
57203
+ display.runFor = function(milliseconds, event ) {
57163
57204
var stoptime = Date.now() + milliseconds;
57164
57205
do {
57165
57206
if (display.quitFlag) return;
57166
- display.runNow();
57207
+ display.runNow(event );
57167
57208
} while (Date.now() < stoptime);
57168
57209
};
57169
57210
if (options.onStart) options.onStart(vm, display, options);
0 commit comments