I’ve been playing around with Oxygene and webassembly…
Attached is some sample Oxygene WASM code that talks to the DOM and has JS calling WASM, and WASM calling JS.
I have a Delphi background and no real WASM/JS experience so this is properly not the right way to do things so interested in any feedback. No real doco or sample code as flying a bit blind.
WASM calling JS
By adding a JS function I can call it from Oxygene as per the code below:
// Call JS functions from WASM
WebAssembly.Global.jslog("test");
on the JS side…
function jslog(something) {
console.log('jslog... ' + something);
}
JS calling WASM
I found that in the html/js code if I made the var ‘program’ a global I could call JS functions from WASM. In Delphi land globals are normally avoided so not sure if this is the right approach ?
On the JS side…
function jsadd( a, b) {
console.log('jsadd... ' + a + b );
window.program.WasmLog('jsadd - window.program.WasmLog');
program.WasmLog('jsadd - program.WasmLog');
}
this calls a WASM method WasmLog
//WASM method that gets call from JS
method WasmLog( s : String );
begin
Console.Write( 'wasmlog... ' + s );
end;
Interesting bit about the above is jsadd can be called from WASM… so WASM calling JS calling WASM!!
WebAssembly.Global.jsadd( 2, 3 ); //JS does not think these are integers
Talking to the DOM
The HTML has a DIV called stage
I can get this element from WASM and add a canvas to the DOM as per below:
var stage := WebAssembly.GetElementById('stage');
if stage = nil then begin
writeLn('Element by ID test is null!');
exit;
end;
var canvas := WebAssembly.CreateElement('canvas');
canvas.id := "TheCanvas";
canvas.width := 800;
canvas.height := 800;
canvas.onclick := new WebAssemblyDelegate( (a) -> CanvasOnClick(a) );
canvas.style := "position: absolute; z-index: 1; background: WhiteSmoke; border: 1px solid; border-color: silver";
stage.appendChild(canvas);
DOM events
Getting DOM events back to WASM was… interesting. The following hooks up the event to an Oxygene method called WindowInKeyDown
var w := WebAssembly.GetWindowObject;
w.onkeydown := new WebAssemblyDelegate( (a) -> WindowOnKeyDown(a) );
and the method
method WindowOnKeyDown( e: EcmaScriptObject );
begin
var lObject := new EcmaScriptObject( WebAssemblyCalls.GetArray(e.Handle, 0) );
var lKey := Double( lObject['keyCode'] );
var text := 'JS DOM Window keydown event ' + 'key:' + lKey.ToString;
CanvasText( text );
Console.Write( text );
end;
The method gets passed a EcmaScriptObject but for some reason I don’t fully understand I have to create another EcmaScriptObject and pass a handle to get to the actual data (eg keycode). I stole this from the sample wasm VCL code which is about all the doco I could find.
Timers
WASM and/or Elements RTL has no concept of a TTimer but JS has this functionality so this was fairly easy to work out once WASM and JS could talk to each other.
I did this with a JS global var ‘thetimer’ for the timer and three JS functions:
function timerstart( a ) {
thetimer = setInterval(timertick, a);
ticks = 0;
console.log('timerstart: ' + a );
}
function timerstop() {
window.clearInterval(thetimer);
console.log('timerstop' );
}
function timertick() {
ticks = ticks + 1;
program.WasmOnTimer( ticks );
//console.log('tick' );
}
timerstart gets called form WASM to start the timer and calls timertick
timertick calls a WASM method when fired
timerstop gets called from WASM and stops the timer (funny that)
I had a lot of fun working this all out. Not really sure where this is heading but Oxygene/wasm sure is entertaining!
Feedback welcome
Canvas.zip (730.4 KB)