wasmjob: added demo to create a button with a click event

This commit is contained in:
mattias 2022-07-12 19:45:43 +02:00
parent 0303bf9da9
commit 211f4021eb
8 changed files with 454 additions and 0 deletions

1
demo/wasienv/button/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
lib

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<Runnable Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="BrowserButton1"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="4">
<Item0 Name="MaintainHTML" Value="1"/>
<Item1 Name="Pas2JSProject" Value="1"/>
<Item2 Name="PasJSLocation" Value="BrowserButton1"/>
<Item3 Name="PasJSWebBrowserProject" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<Units>
<Unit>
<Filename Value="BrowserButton1.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="../button/index.html"/>
<IsPartOfProject Value="True"/>
<CustomData Count="1">
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="../dom/job_browser.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="JOB_Browser"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target FileExt=".js">
<Filename Value="BrowserButton1"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="../dom"/>
<UnitOutputDirectory Value="js"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<AllowLabel Value="False"/>
<CPPInline Value="False"/>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="browser"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<UseLineInfoUnit Value="False"/>
</Debugging>
</Linking>
<Other>
<CustomOptions Value="-Jeutf-8
-Jirtl.js
-Jc
-Jminclude
-dVerboseJOB"/>
<OtherDefines Count="1">
<Define0 Value="VerboseJOB"/>
</OtherDefines>
<CompilerPath Value="$(pas2js)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions>
<Item>
<Name Value="EAbort"/>
</Item>
<Item>
<Name Value="ECodetoolError"/>
</Item>
<Item>
<Name Value="EFOpenError"/>
</Item>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,127 @@
program BrowserButton1;
{$mode objfpc}
uses
BrowserConsole, BrowserApp, JS, Classes, SysUtils, Web, WebAssembly, Types,
WasiEnv, JOB_Browser, JOB_Shared;
Type
{ TMyApplication }
TMyApplication = class(TBrowserApplication)
// todo: most of this code should be moved a TBrowserJOBApplication
Private
FWasiEnv: TPas2JSWASIEnvironment;
FMemory : TJSWebAssemblyMemory; // Memory of webassembly
FTable : TJSWebAssemblyTable; // Table of exported functions
FWADomBridge : TJSObjectBridge;
function CreateWebAssembly(Path: string; ImportObject: TJSObject
): TJSPromise;
procedure DoWrite(Sender: TObject; const aOutput: String);
function InitEnv(aValue: JSValue): JSValue;
procedure InitWebAssembly;
Public
Constructor Create(aOwner : TComponent); override;
Destructor Destroy; override;
procedure DoRun; override;
end;
function TMyApplication.InitEnv(aValue: JSValue): JSValue;
Var
Module : TJSInstantiateResult absolute aValue;
Exps : TWASIExports;
InitFunc: TProc;
begin
Result:=True;
FWasiEnv.Instance:=Module.Instance;
Exps := TWASIExports(TJSObject(Module.Instance.exports_));
//writeln('TMyApplication.InitEnv wasm exports=',TJSObject.keys(Exps));
FWADomBridge.WasiExports:=Exps;
// init the library
InitFunc:=TProc(Exps.functions['_initialize']);
writeln('TMyApplication.InitEnv ');
InitFunc();
end;
{ TMyApplication }
procedure TMyApplication.DoWrite(Sender: TObject; const aOutput: String);
begin
Writeln(aOutput);
end;
constructor TMyApplication.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
FWasiEnv:=TPas2JSWASIEnvironment.Create;
FWasiEnv.OnStdErrorWrite:=@DoWrite;
FWasiEnv.OnStdOutputWrite:=@DoWrite;
FWADomBridge:=TJSObjectBridge.Create(FWasiEnv);
end;
function TMyApplication.CreateWebAssembly(Path: string; ImportObject: TJSObject): TJSPromise;
begin
Result:=window.fetch(Path)._then(Function (res : jsValue) : JSValue
begin
Result:=TJSResponse(Res).arrayBuffer._then(Function (res2 : jsValue) : JSValue
begin
Result:=TJSWebAssembly.instantiate(TJSArrayBuffer(res2),ImportObject);
end,Nil)
end,Nil
);
end;
procedure TMyApplication.InitWebAssembly;
Var
mDesc: TJSWebAssemblyMemoryDescriptor;
tDesc: TJSWebAssemblyTableDescriptor;
ImportObj : TJSObject;
begin
// Setup memory
mDesc.initial:=256;
mDesc.maximum:=256;
FMemory:=TJSWebAssemblyMemory.New(mDesc);
// Setup table
tDesc.initial:=0;
tDesc.maximum:=0;
tDesc.element:='anyfunc';
FTable:=TJSWebAssemblyTable.New(tDesc);
// Setup ImportObject
ImportObj:=new([
'js', new([
'mem', FMemory,
'tbl', FTable
])
]);
FWasiEnv.AddImports(ImportObj);
CreateWebAssembly('WasiButton1.wasm',ImportObj)._then(@InitEnv);
end;
destructor TMyApplication.Destroy;
begin
FreeAndNil(FWasiEnv);
inherited Destroy;
end;
procedure TMyApplication.DoRun;
begin
// Your code here
Terminate;
InitWebAssembly;
end;
var
Application : TMyApplication;
begin
Application:=TMyApplication.Create(nil);
Application.Initialize;
Application.Run;
end.

View File

@ -0,0 +1,7 @@
Demo showing how to create a button from Wasi using JOB (JavaScript Object Bridge).
It contains the two projects
WasiButton1.lpi - Wasi
BrowserButton1.lpi - JavaScript/Pas2js

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="WasiButton1"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<Units>
<Unit>
<Filename Value="WasiButton1.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="../dom/job_wasm.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="JOB_WAsm"/>
</Unit>
<Unit>
<Filename Value="../dom/job_web.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="JOB_Web"/>
</Unit>
<Unit>
<Filename Value="../dom/job_js.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="JOB_JS"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="WasiButton1.wasm" ApplyConventions="False"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="../dom"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>
<TargetCPU Value="wasm32"/>
<TargetOS Value="wasi"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
</Debugging>
<Options>
<ExecutableType Value="Library"/>
</Options>
</Linking>
<Other>
<OtherDefines Count="1">
<Define0 Value="VerboseInvokeJSArgs"/>
</OtherDefines>
<CompilerPath Value="/usr/lib/fpc/3.3.1/ppcrosswasm32"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions>
<Item>
<Name Value="EAbort"/>
</Item>
<Item>
<Name Value="ECodetoolError"/>
</Item>
<Item>
<Name Value="EFOpenError"/>
</Item>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,73 @@
library WasiButton1;
{$mode objfpc}
{$h+}
{$codepage UTF8}
uses
SysUtils, JOB_WAsm, JOB_Shared, JOB_Web, JOB_JS;
type
{ TWasmApp }
TWasmApp = class
private
function OnButtonClick(Event: IJSEventListenerEvent): boolean;
public
procedure Run;
end;
{ TApplication }
function TWasmApp.OnButtonClick(Event: IJSEventListenerEvent): boolean;
begin
writeln('TWasmApp.OnButtonClick ');
if Event=nil then ;
JSWindow.Alert('You triggered TWasmApp.OnButtonClick');
Result:=true;
end;
procedure TWasmApp.Run;
var
JSDiv: IJSHTMLDivElement;
JSButton: IJSHTMLButtonElement;
begin
writeln('TWasmApp.Run getElementById "playground" ...');
// get reference of HTML element "playground" and type cast it to Div
JSDiv:=TJSHTMLDivElement.Cast(JSDocument.getElementById('playground'));
// create button
writeln('TWasmApp.Run create button ...');
JSButton:=TJSHTMLButtonElement.Cast(JSDocument.createElement('button'));
writeln('TWasmApp.Run set button caption ...');
JSButton.InnerHTML:='Click me!';
// add button to div
writeln('TWasmApp.Run add button to div ...');
JSDiv.append(JSButton);
// add event listener OnButtonClick
writeln('TWasmApp.Run addEventListener OnButtonClick ...');
JSButton.addEventListener('click',@OnButtonClick);
writeln('TWasmApp.Run END');
end;
// workaround: fpc wasm does not yet support exporting functions from units
function JOBCallback(const Func: TJOBCallback; Data, Code: Pointer; Args: PByte): PByte;
begin
Result:=JOB_WAsm.JOBCallback(Func,Data,Code,Args);
end;
exports
JOBCallback;
var
Application: TWasmApp;
begin
Application:=TWasmApp.Create;
Application.Run;
end.

1
demo/wasienv/button/bulma.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,52 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FPC-Webassembly Demo creating a button via JOB</title>
<link href="bulma.min.css" rel="stylesheet">
<script src="BrowserButton1.js"></script>
<style>
.source {
/* width: 730px; */
margin: -45px auto;
font-size: 0.9em;
}
.source-inner {
display: flex;
justify-content: space-between;
align-items: center;
/* width: 482px; */
}
</style>
</head>
<body>
<div class="section py-4">
<h1 class="title is-3">Test Area</h1>
<div class="box" id="playground">Playground</div>
</div>
<div class="section py-4">
<h1 class="title is-3">Console output</h1>
<div class="box" id="pasjsconsole"></div>
</div>
<!-- <hr> -->
<div class="section">
<div class="source">
<div class="source-inner">
<div>
<p>Created using &nbsp; <a target="_blank" href="https://wiki.freepascal.org/pas2js">pas2js.</a> </p>
<p>Pas2JS Sources: &nbsp; <a target="new" href="BrowserButton1.lpr">Pas2JS Program</a></p>
<p>Webassembly Sources: &nbsp; <a target="new" href="WasiButton1.lpr">FPC Program</a></p>
</div>
</div>
</div>
</div>
<script>
rtl.showUncaughtExceptions=true;
rtl.run();
</script>
</body>
</html>