diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 85015a6599..ac1d9b9189 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -120,7 +120,8 @@ jobs:
- name: Install Haxe dependencies
run: |
- haxelib install hxcpp 4.2.1 --quiet
+ curl -LO https://github.com/HaxeFoundation/hxcpp/releases/download/v4.3.45/hxcpp-4.3.45.zip
+ haxelib install ./hxcpp-4.3.45.zip --quiet
haxelib install format --quiet
haxelib install hxp --quiet
@@ -137,6 +138,7 @@ jobs:
- name: Rebuild Lime (macOS)
run: |
lime rebuild macos -clean -release -64 -nocolor -verbose -nocffi
+ lime rebuild macos -clean -release -arm64 -nocolor -verbose -nocffi
lime rebuild hl -clean -release -nocolor -verbose -nocffi
- uses: actions/upload-artifact@v3
@@ -147,6 +149,14 @@ jobs:
!**/.gitignore
if-no-files-found: error
+ - uses: actions/upload-artifact@v3
+ with:
+ name: MacArm64-NDLL
+ path: |
+ ndll/MacArm64/
+ !**/.gitignore
+ if-no-files-found: error
+
- uses: actions/upload-artifact@v3
with:
name: Mac64-Hashlink
@@ -466,6 +476,11 @@ jobs:
name: Mac64-NDLL
path: ndll/Mac64/
+ - uses: actions/download-artifact@v3
+ with:
+ name: MacArm64-NDLL
+ path: ndll/MacArm64/
+
- uses: actions/download-artifact@v3
with:
name: Windows-NDLL
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f181c8998..133f1c3d5b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
Changelog
=========
+8.1.3 (07/22/2024)
+------------------
+
+* Fixed Unicode conversion issues with various APIs, including as clipboard, window text, file dialogs, and fonts.
+* Fixed Android builds with the `-emulator` flag that broke when using newer Android SDKs.
+* Fixed "Could not link to neko" error message on macOS when lime.ndll is built with Xcode 15.
+* Fixed system environment variables with the same name as common targets conflicting with other targets.
+* Fixed `` and `` output confusing Haxe's completion server. Now skipped when `display` is defined.
+* Fixed `Clipboard.text` incorrectly getting cleared to null when targeting HTML5.
+* Fixed integer value parsing in _project.xml_, including immediately reporting errors for invalid values.
+* Fixed incorrect path discovery for NDLL files that found source directory instead.
+* Fixed missing error message if HXP project class name does not match file name.
+* Fixed unspecified behavior from `null` keys in `ObjectPool`.
+* Fixed some issues when running Haxe built natively for Apple Silicon.
+* Fixed missing cancellation of vibration on Android when app is paused or destroyed.
+* Fixed static linking of native curl library on macOS.
+* Fixed deprecation warnings in Android Gradle builds.
+
8.1.2 (03/13/2024)
------------------
diff --git a/NOTICE.md b/NOTICE.md
index 9eba9c755b..a2f4b4de68 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -104,6 +104,9 @@ This product bundles FileSaver.js 1.3.3, which is available under an
This product bundles pako 1.0.2, which is available under an
"MIT" license. For details, see [dependencies/pako.min.js](dependencies/pako.min.js).
+This product bundles LZMA-JS 2.3.2, which is available under an
+"MIT" license. For details, see [dependencies/lzma_worker-min.js](dependencies/lzma_worker-min.js).
+
This product bundles stats.js r16, which is available under an
"MIT" license. For details, see [dependencies/stats.min.js](dependencies/stats.min.js).
diff --git a/dependencies/extension-api/build.gradle b/dependencies/extension-api/build.gradle
index fe526ccdb8..411c05cd0b 100644
--- a/dependencies/extension-api/build.gradle
+++ b/dependencies/extension-api/build.gradle
@@ -12,6 +12,7 @@ buildscript {
apply plugin: 'com.android.library'
android {
+ namespace 'org.haxe.extension'
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
diff --git a/dependencies/extension-api/src/main/AndroidManifest.xml b/dependencies/extension-api/src/main/AndroidManifest.xml
index 5bc5fecfec..b2d3ea1235 100644
--- a/dependencies/extension-api/src/main/AndroidManifest.xml
+++ b/dependencies/extension-api/src/main/AndroidManifest.xml
@@ -1,4 +1,2 @@
-
-
-
+
diff --git a/dependencies/lzma_worker-min.js b/dependencies/lzma_worker-min.js
new file mode 100644
index 0000000000..8aa7383403
--- /dev/null
+++ b/dependencies/lzma_worker-min.js
@@ -0,0 +1,2 @@
+var e=function(){"use strict";function r(e,r){postMessage({action:xt,cbn:r,result:e})}function t(e){var r=[];return r[e-1]=void 0,r}function o(e,r){return i(e[0]+r[0],e[1]+r[1])}function n(e,r){return u(~~Math.max(Math.min(e[1]/Ot,2147483647),-2147483648)&~~Math.max(Math.min(r[1]/Ot,2147483647),-2147483648),c(e)&c(r))}function s(e,r){var t,o;return e[0]==r[0]&&e[1]==r[1]?0:(t=0>e[1],o=0>r[1],t&&!o?-1:!t&&o?1:h(e,r)[1]<0?-1:1)}function i(e,r){var t,o;for(r%=0x10000000000000000,e%=0x10000000000000000,t=r%Ot,o=Math.floor(e/Ot)*Ot,r=r-t+o,e=e-o+t;0>e;)e+=Ot,r-=Ot;for(;e>4294967295;)e-=Ot,r+=Ot;for(r%=0x10000000000000000;r>0x7fffffff00000000;)r-=0x10000000000000000;for(;-0x8000000000000000>r;)r+=0x10000000000000000;return[e,r]}function _(e,r){return e[0]==r[0]&&e[1]==r[1]}function a(e){return e>=0?[e,0]:[e+Ot,-Ot]}function c(e){return e[0]>=2147483648?~~Math.max(Math.min(e[0]-Ot,2147483647),-2147483648):~~Math.max(Math.min(e[0],2147483647),-2147483648)}function u(e,r){var t,o;return t=e*Ot,o=r,0>r&&(o+=Ot),[o,t]}function f(e){return 30>=e?1<e[1])throw Error("Neg");return s=f(r),o=e[1]*s%0x10000000000000000,n=e[0]*s,t=n-n%Ot,o+=t,n-=t,o>=0x8000000000000000&&(o-=0x10000000000000000),[n,o]}function d(e,r){var t;return r&=63,t=f(r),i(Math.floor(e[0]/t),e[1]/t)}function p(e,r){var t;return r&=63,t=d(e,r),0>e[1]&&(t=o(t,m([2,0],63-r))),t}function h(e,r){return i(e[0]-r[0],e[1]-r[1])}function P(e,r){return e.Mc=r,e.Lc=0,e.Yb=r.length,e}function l(e){return e.Lc>=e.Yb?-1:255&e.Mc[e.Lc++]}function v(e,r,t,o){return e.Lc>=e.Yb?-1:(o=Math.min(o,e.Yb-e.Lc),M(e.Mc,e.Lc,r,t,o),e.Lc+=o,o)}function B(e){return e.Mc=t(32),e.Yb=0,e}function S(e){var r=e.Mc;return r.length=e.Yb,r}function g(e,r){e.Mc[e.Yb++]=r<<24>>24}function k(e,r,t,o){M(r,t,e.Mc,e.Yb,o),e.Yb+=o}function R(e,r,t,o,n){var s;for(s=r;t>s;++s)o[n++]=e.charCodeAt(s)}function M(e,r,t,o,n){for(var s=0;n>s;++s)t[o+s]=e[r+s]}function D(e,r){Ar(r,1<a;a+=8)g(o,255&c(d(n,a)));r.yb=(_.W=0,_.oc=t,_.pc=0,Mr(_),_.d.Ab=o,Fr(_),wr(_),br(_),_.$.rb=_.n+1-2,Qr(_.$,1<<_.Y),_.i.rb=_.n+1-2,Qr(_.i,1<<_.Y),void(_.g=Gt),X({},_))}function w(e,r,t){return e.Nb=B({}),b(e,P({},r),e.Nb,a(r.length),t),e}function E(e,r,t){var o,n,s,i,_="",c=[];for(n=0;5>n;++n){if(s=l(r),-1==s)throw Error("truncated input");c[n]=s<<24>>24}if(o=ir({}),!ar(o,c))throw Error("corrupted input");for(n=0;64>n;n+=8){if(s=l(r),-1==s)throw Error("truncated input");s=s.toString(16),1==s.length&&(s="0"+s),_=s+""+_}/^0+$|^f+$/i.test(_)?e.Tb=At:(i=parseInt(_,16),e.Tb=i>4294967295?At:a(i)),e.yb=nr(o,r,t,e.Tb)}function L(e,r){return e.Nb=B({}),E(e,P({},r),e.Nb),e}function y(e,r,o,n){var s;e.Bc=r,e._b=o,s=r+o+n,(null==e.c||e.Kb!=s)&&(e.c=null,e.Kb=s,e.c=t(e.Kb)),e.H=e.Kb-o}function C(e,r){return e.c[e.f+e.o+r]}function z(e,r,t,o){var n,s;for(e.T&&e.o+r+o>e.h&&(o=e.h-(e.o+r)),++t,s=e.f+e.o+r,n=0;o>n&&e.c[s+n]==e.c[s+n-t];++n);return n}function F(e){return e.h-e.o}function I(e){var r,t,o;for(o=e.f+e.o-e.Bc,o>0&&--o,t=e.f+e.h-o,r=0;t>r;++r)e.c[r]=e.c[o+r];e.f-=o}function x(e){var r;++e.o,e.o>e.zb&&(r=e.f+e.o,r>e.H&&I(e),N(e))}function N(e){var r,t,o;if(!e.T)for(;;){if(o=-e.f+e.Kb-e.h,!o)return;if(r=v(e.cc,e.c,e.f+e.h,o),-1==r)return e.zb=e.h,t=e.f+e.zb,t>e.H&&(e.zb=e.H-e.f),void(e.T=1);e.h+=r,e.h>=e.o+e._b&&(e.zb=e.h-e._b)}}function O(e,r){e.f+=r,e.zb-=r,e.o-=r,e.h-=r}function A(e,r,o,n,s){var i,_,a;1073741567>r&&(e.Fc=16+(n>>1),a=~~((r+o+n+s)/2)+256,y(e,r+o,n+s,a),e.ob=n,i=r+1,e.p!=i&&(e.L=t(2*(e.p=i))),_=65536,e.qb&&(_=r-1,_|=_>>1,_|=_>>2,_|=_>>4,_|=_>>8,_>>=1,_|=65535,_>16777216&&(_>>=1),e.Ec=_,++_,_+=e.R),_!=e.rc&&(e.ub=t(e.rc=_)))}function H(e,r){var t,o,n,s,i,_,a,c,u,f,m,d,p,h,P,l,v,B,S,g,k;if(e.h>=e.o+e.ob)h=e.ob;else if(h=e.h-e.o,e.xb>h)return W(e),0;for(v=0,P=e.o>e.p?e.o-e.p:0,o=e.f+e.o,l=1,c=0,u=0,e.qb?(k=Tt[255&e.c[o]]^255&e.c[o+1],c=1023&k,k^=(255&e.c[o+2])<<8,u=65535&k,f=(k^Tt[255&e.c[o+3]]<<5)&e.Ec):f=255&e.c[o]^(255&e.c[o+1])<<8,n=e.ub[e.R+f]||0,e.qb&&(s=e.ub[c]||0,i=e.ub[1024+u]||0,e.ub[c]=e.o,e.ub[1024+u]=e.o,s>P&&e.c[e.f+s]==e.c[o]&&(r[v++]=l=2,r[v++]=e.o-s-1),i>P&&e.c[e.f+i]==e.c[o]&&(i==s&&(v-=2),r[v++]=l=3,r[v++]=e.o-i-1,s=i),0!=v&&s==n&&(v-=2,l=1)),e.ub[e.R+f]=e.o,S=(e.k<<1)+1,g=e.k<<1,d=p=e.w,0!=e.w&&n>P&&e.c[e.f+n+e.w]!=e.c[o+e.w]&&(r[v++]=l=e.w,r[v++]=e.o-n-1),t=e.Fc;;){if(P>=n||0==t--){e.L[S]=e.L[g]=0;break}if(a=e.o-n,_=(e.k>=a?e.k-a:e.k-a+e.p)<<1,B=e.f+n,m=p>d?d:p,e.c[B+m]==e.c[o+m]){for(;++m!=h&&e.c[B+m]==e.c[o+m];);if(m>l&&(r[v++]=l=m,r[v++]=a-1,m==h)){e.L[g]=e.L[_],e.L[S]=e.L[_+1];break}}(255&e.c[o+m])>(255&e.c[B+m])?(e.L[g]=n,g=_+1,n=e.L[g],p=m):(e.L[S]=n,S=_,n=e.L[S],d=m)}return W(e),v}function G(e){e.f=0,e.o=0,e.h=0,e.T=0,N(e),e.k=0,O(e,-1)}function W(e){var r;++e.k>=e.p&&(e.k=0),x(e),1073741823==e.o&&(r=e.o-e.p,T(e.L,2*e.p,r),T(e.ub,e.rc,r),O(e,r))}function T(e,r,t){var o,n;for(o=0;r>o;++o)n=e[o]||0,t>=n?n=0:n-=t,e[o]=n}function Z(e,r){e.qb=r>2,e.qb?(e.w=0,e.xb=4,e.R=66560):(e.w=2,e.xb=3,e.R=0)}function Y(e,r){var t,o,n,s,i,_,a,c,u,f,m,d,p,h,P,l,v;do{if(e.h>=e.o+e.ob)d=e.ob;else if(d=e.h-e.o,e.xb>d){W(e);continue}for(p=e.o>e.p?e.o-e.p:0,o=e.f+e.o,e.qb?(v=Tt[255&e.c[o]]^255&e.c[o+1],_=1023&v,e.ub[_]=e.o,v^=(255&e.c[o+2])<<8,a=65535&v,e.ub[1024+a]=e.o,c=(v^Tt[255&e.c[o+3]]<<5)&e.Ec):c=255&e.c[o]^(255&e.c[o+1])<<8,n=e.ub[e.R+c],e.ub[e.R+c]=e.o,P=(e.k<<1)+1,l=e.k<<1,f=m=e.w,t=e.Fc;;){if(p>=n||0==t--){e.L[P]=e.L[l]=0;break}if(i=e.o-n,s=(e.k>=i?e.k-i:e.k-i+e.p)<<1,h=e.f+n,u=m>f?f:m,e.c[h+u]==e.c[o+u]){for(;++u!=d&&e.c[h+u]==e.c[o+u];);if(u==d){e.L[l]=e.L[s],e.L[P]=e.L[s+1];break}}(255&e.c[o+u])>(255&e.c[h+u])?(e.L[l]=n,l=s+1,n=e.L[l],m=u):(e.L[P]=n,P=s,n=e.L[P],f=u)}W(e)}while(0!=--r)}function V(e,r,t){var o=e.o-r-1;for(0>o&&(o+=e.M);0!=t;--t)o>=e.M&&(o=0),e.Lb[e.o++]=e.Lb[o++],e.o>=e.M&&$(e)}function j(e,r){(null==e.Lb||e.M!=r)&&(e.Lb=t(r)),e.M=r,e.o=0,e.h=0}function $(e){var r=e.o-e.h;r&&(k(e.cc,e.Lb,e.h,r),e.o>=e.M&&(e.o=0),e.h=e.o)}function K(e,r){var t=e.o-r-1;return 0>t&&(t+=e.M),e.Lb[t]}function q(e,r){e.Lb[e.o++]=r,e.o>=e.M&&$(e)}function J(e){$(e),e.cc=null}function Q(e){return e-=2,4>e?e:3}function U(e){return 4>e?0:10>e?e-3:e-6}function X(e,r){return e.cb=r,e.Z=null,e.zc=1,e}function er(e,r){return e.Z=r,e.cb=null,e.zc=1,e}function rr(e){if(!e.zc)throw Error("bad state");return e.cb?or(e):tr(e),e.zc}function tr(e){var r=sr(e.Z);if(-1==r)throw Error("corrupted input");e.Pb=At,e.Pc=e.Z.g,(r||s(e.Z.Nc,Gt)>=0&&s(e.Z.g,e.Z.Nc)>=0)&&($(e.Z.B),J(e.Z.B),e.Z.e.Ab=null,e.zc=0)}function or(e){Rr(e.cb,e.cb.Xb,e.cb.uc,e.cb.Kc),e.Pb=e.cb.Xb[0],e.cb.Kc[0]&&(Or(e.cb),e.zc=0)}function nr(e,r,t,o){return e.e.Ab=r,J(e.B),e.B.cc=t,_r(e),e.U=0,e.ib=0,e.Jc=0,e.Ic=0,e.Qc=0,e.Nc=o,e.g=Gt,e.jc=0,er({},e)}function sr(e){var r,t,n,i,_,u;if(u=c(e.g)&e.Dc,vt(e.e,e.Gb,(e.U<<4)+u)){if(vt(e.e,e.Zb,e.U))n=0,vt(e.e,e.Cb,e.U)?(vt(e.e,e.Db,e.U)?(vt(e.e,e.Eb,e.U)?(t=e.Qc,e.Qc=e.Ic):t=e.Ic,e.Ic=e.Jc):t=e.Jc,e.Jc=e.ib,e.ib=t):vt(e.e,e.pb,(e.U<<4)+u)||(e.U=7>e.U?9:11,n=1),n||(n=mr(e.sb,e.e,u)+2,e.U=7>e.U?8:11);else if(e.Qc=e.Ic,e.Ic=e.Jc,e.Jc=e.ib,n=2+mr(e.Rb,e.e,u),e.U=7>e.U?7:10,_=at(e.kb[Q(n)],e.e),_>=4){if(i=(_>>1)-1,e.ib=(2|1&_)<_)e.ib+=ut(e.kc,e.ib-_-1,e.e,i);else if(e.ib+=Bt(e.e,i-4)<<4,e.ib+=ct(e.Fb,e.e),0>e.ib)return-1==e.ib?1:-1}else e.ib=_;if(s(a(e.ib),e.g)>=0||e.ib>=e.nb)return-1;V(e.B,e.ib,n),e.g=o(e.g,a(n)),e.jc=K(e.B,0)}else r=Pr(e.gb,c(e.g),e.jc),e.jc=7>e.U?vr(r,e.e):Br(r,e.e,K(e.B,e.ib)),q(e.B,e.jc),e.U=U(e.U),e.g=o(e.g,Wt);return 0}function ir(e){e.B={},e.e={},e.Gb=t(192),e.Zb=t(12),e.Cb=t(12),e.Db=t(12),e.Eb=t(12),e.pb=t(192),e.kb=t(4),e.kc=t(114),e.Fb=_t({},4),e.Rb=dr({}),e.sb=dr({}),e.gb={};for(var r=0;4>r;++r)e.kb[r]=_t({},6);return e}function _r(e){e.B.h=0,e.B.o=0,gt(e.Gb),gt(e.pb),gt(e.Zb),gt(e.Cb),gt(e.Db),gt(e.Eb),gt(e.kc),lr(e.gb);for(var r=0;4>r;++r)gt(e.kb[r].G);pr(e.Rb),pr(e.sb),gt(e.Fb.G),St(e.e)}function ar(e,r){var t,o,n,s,i,_,a;if(5>r.length)return 0;for(a=255&r[0],n=a%9,_=~~(a/9),s=_%5,i=~~(_/5),t=0,o=0;4>o;++o)t+=(255&r[1+o])<<8*o;return t>99999999||!ur(e,n,s,i)?0:cr(e,t)}function cr(e,r){return 0>r?0:(e.Ob!=r&&(e.Ob=r,e.nb=Math.max(e.Ob,1),j(e.B,Math.max(e.nb,4096))),1)}function ur(e,r,t,o){if(r>8||t>4||o>4)return 0;hr(e.gb,t,r);var n=1<e.O;++e.O)e.ec[e.O]=_t({},3),e.hc[e.O]=_t({},3)}function mr(e,r,t){if(!vt(r,e.wc,0))return at(e.ec[t],r);var o=8;return o+=vt(r,e.wc,1)?8+at(e.tc,r):at(e.hc[t],r)}function dr(e){return e.wc=t(2),e.ec=t(16),e.hc=t(16),e.tc=_t({},8),e.O=0,e}function pr(e){gt(e.wc);for(var r=0;e.O>r;++r)gt(e.ec[r].G),gt(e.hc[r].G);gt(e.tc.G)}function hr(e,r,o){var n,s;if(null==e.V||e.u!=o||e.I!=r)for(e.I=r,e.qc=(1<n;++n)e.V[n]=Sr({})}function Pr(e,r,t){return e.V[((r&e.qc)<>>8-e.u)]}function lr(e){var r,t;for(t=1<r;++r)gt(e.V[r].Ib)}function vr(e,r){var t=1;do t=t<<1|vt(r,e.Ib,t);while(256>t);return t<<24>>24}function Br(e,r,t){var o,n,s=1;do if(n=t>>7&1,t<<=1,o=vt(r,e.Ib,(1+n<<8)+s),s=s<<1|o,n!=o){for(;256>s;)s=s<<1|vt(r,e.Ib,s);break}while(256>s);return s<<24>>24}function Sr(e){return e.Ib=t(768),e}function gr(e,r){var t,o,n,s;e.jb=r,n=e.a[r].r,o=e.a[r].j;do e.a[r].t&&(st(e.a[n]),e.a[n].r=n-1,e.a[r].Ac&&(e.a[n-1].t=0,e.a[n-1].r=e.a[r].r2,e.a[n-1].j=e.a[r].j2)),s=n,t=o,o=e.a[s].j,n=e.a[s].r,e.a[s].j=t,e.a[s].r=r,r=s;while(r>0);return e.mb=e.a[0].j,e.q=e.a[0].r}function kr(e){e.l=0,e.J=0;for(var r=0;4>r;++r)e.v[r]=0}function Rr(e,r,t,n){var i,u,f,m,d,p,P,l,v,B,S,g,k,R,M;if(r[0]=Gt,t[0]=Gt,n[0]=1,e.oc&&(e.b.cc=e.oc,G(e.b),e.W=1,e.oc=null),!e.pc){if(e.pc=1,R=e.g,_(e.g,Gt)){if(!F(e.b))return void Er(e,c(e.g));xr(e),k=c(e.g)&e.y,kt(e.d,e.C,(e.l<<4)+k,0),e.l=U(e.l),f=C(e.b,-e.s),rt(Xr(e.A,c(e.g),e.J),e.d,f),e.J=f,--e.s,e.g=o(e.g,Wt)}if(!F(e.b))return void Er(e,c(e.g));for(;;){if(P=Lr(e,c(e.g)),B=e.mb,k=c(e.g)&e.y,u=(e.l<<4)+k,1==P&&-1==B)kt(e.d,e.C,u,0),f=C(e.b,-e.s),M=Xr(e.A,c(e.g),e.J),7>e.l?rt(M,e.d,f):(v=C(e.b,-e.v[0]-1-e.s),tt(M,e.d,v,f)),e.J=f,e.l=U(e.l);else{if(kt(e.d,e.C,u,1),4>B){if(kt(e.d,e.bb,e.l,1),B?(kt(e.d,e.hb,e.l,1),1==B?kt(e.d,e.Ub,e.l,0):(kt(e.d,e.Ub,e.l,1),kt(e.d,e.vc,e.l,B-2))):(kt(e.d,e.hb,e.l,0),1==P?kt(e.d,e._,u,0):kt(e.d,e._,u,1)),1==P?e.l=7>e.l?9:11:(Kr(e.i,e.d,P-2,k),e.l=7>e.l?8:11),m=e.v[B],0!=B){for(p=B;p>=1;--p)e.v[p]=e.v[p-1];e.v[0]=m}}else{for(kt(e.d,e.bb,e.l,0),e.l=7>e.l?7:10,Kr(e.$,e.d,P-2,k),B-=4,g=Tr(B),l=Q(P),mt(e.K[l],e.d,g),g>=4&&(d=(g>>1)-1,i=(2|1&g)<g?Pt(e.Sb,i-g-1,e.d,d,S):(Rt(e.d,S>>4,d-4),pt(e.S,e.d,15&S),++e.Qb)),m=B,p=3;p>=1;--p)e.v[p]=e.v[p-1];e.v[0]=m,++e.Mb}e.J=C(e.b,P-1-e.s)}if(e.s-=P,e.g=o(e.g,a(P)),!e.s){if(e.Mb>=128&&wr(e),e.Qb>=16&&br(e),r[0]=e.g,t[0]=Mt(e.d),!F(e.b))return void Er(e,c(e.g));if(s(h(e.g,R),[4096,0])>=0)return e.pc=0,void(n[0]=0)}}}}function Mr(e){var r,t;e.b||(r={},t=4,e.X||(t=2),Z(r,t),e.b=r),Ur(e.A,e.eb,e.fb),(e.ab!=e.wb||e.Hb!=e.n)&&(A(e.b,e.ab,4096,e.n,274),e.wb=e.ab,e.Hb=e.n)}function Dr(e){var r;for(e.v=t(4),e.a=[],e.d={},e.C=t(192),e.bb=t(12),e.hb=t(12),e.Ub=t(12),e.vc=t(12),e._=t(192),e.K=[],e.Sb=t(114),e.S=ft({},4),e.$=qr({}),e.i=qr({}),e.A={},e.m=[],e.P=[],e.lb=[],e.nc=t(16),e.x=t(4),e.Q=t(4),e.Xb=[Gt],e.uc=[Gt],e.Kc=[0],e.fc=t(5),e.yc=t(128),e.vb=0,e.X=1,e.D=0,e.Hb=-1,e.mb=0,r=0;4096>r;++r)e.a[r]={};for(r=0;4>r;++r)e.K[r]=ft({},6);return e}function br(e){for(var r=0;16>r;++r)e.nc[r]=ht(e.S,r);e.Qb=0}function wr(e){var r,t,o,n,s,i,_,a;for(n=4;128>n;++n)i=Tr(n),o=(i>>1)-1,r=(2|1&i)<s;++s){for(t=e.K[s],_=s<<6,i=0;e.$b>i;++i)e.P[_+i]=dt(t,i);for(i=14;e.$b>i;++i)e.P[_+i]+=(i>>1)-1-4<<6;for(a=128*s,n=0;4>n;++n)e.lb[a+n]=e.P[_+n];for(;128>n;++n)e.lb[a+n]=e.P[_+Tr(n)]+e.yc[n]}e.Mb=0}function Er(e,r){Nr(e),Wr(e,r&e.y);for(var t=0;5>t;++t)bt(e.d)}function Lr(e,r){var t,o,n,s,i,_,a,c,u,f,m,d,p,h,P,l,v,B,S,g,k,R,M,D,b,w,E,L,y,I,x,N,O,A,H,G,W,T,Z,Y,V,j,$,K,q,J,Q,X,er,rr;if(e.jb!=e.q)return p=e.a[e.q].r-e.q,e.mb=e.a[e.q].j,e.q=e.a[e.q].r,p;if(e.q=e.jb=0,e.N?(d=e.vb,e.N=0):d=xr(e),E=e.D,b=F(e.b)+1,2>b)return e.mb=-1,1;for(b>273&&(b=273),Y=0,u=0;4>u;++u)e.x[u]=e.v[u],e.Q[u]=z(e.b,-1,e.x[u],273),e.Q[u]>e.Q[Y]&&(Y=u);if(e.Q[Y]>=e.n)return e.mb=Y,p=e.Q[Y],Ir(e,p-1),p;if(d>=e.n)return e.mb=e.m[E-1]+4,Ir(e,d-1),d;if(a=C(e.b,-1),v=C(e.b,-e.v[0]-1-1),2>d&&a!=v&&2>e.Q[Y])return e.mb=-1,1;if(e.a[0].Hc=e.l,A=r&e.y,e.a[1].z=Yt[e.C[(e.l<<4)+A]>>>2]+nt(Xr(e.A,r,e.J),e.l>=7,v,a),st(e.a[1]),B=Yt[2048-e.C[(e.l<<4)+A]>>>2],Z=B+Yt[2048-e.bb[e.l]>>>2],v==a&&(V=Z+zr(e,e.l,A),e.a[1].z>V&&(e.a[1].z=V,it(e.a[1]))),m=d>=e.Q[Y]?d:e.Q[Y],2>m)return e.mb=e.a[1].j,1;e.a[1].r=0,e.a[0].bc=e.x[0],e.a[0].ac=e.x[1],e.a[0].dc=e.x[2],e.a[0].lc=e.x[3],f=m;do e.a[f--].z=268435455;while(f>=2);for(u=0;4>u;++u)if(T=e.Q[u],!(2>T)){G=Z+Cr(e,u,e.l,A);do s=G+Jr(e.i,T-2,A),x=e.a[T],x.z>s&&(x.z=s,x.r=0,x.j=u,x.t=0);while(--T>=2)}if(D=B+Yt[e.bb[e.l]>>>2],f=e.Q[0]>=2?e.Q[0]+1:2,d>=f){for(L=0;f>e.m[L];)L+=2;for(;c=e.m[L+1],s=D+yr(e,c,f,A),x=e.a[f],x.z>s&&(x.z=s,x.r=0,x.j=c+4,x.t=0),f!=e.m[L]||(L+=2,L!=E);++f);}for(t=0;;){if(++t,t==m)return gr(e,t);if(S=xr(e),E=e.D,S>=e.n)return e.vb=S,e.N=1,gr(e,t);if(++r,O=e.a[t].r,e.a[t].t?(--O,e.a[t].Ac?($=e.a[e.a[t].r2].Hc,$=4>e.a[t].j2?7>$?8:11:7>$?7:10):$=e.a[O].Hc,$=U($)):$=e.a[O].Hc,O==t-1?$=e.a[t].j?U($):7>$?9:11:(e.a[t].t&&e.a[t].Ac?(O=e.a[t].r2,N=e.a[t].j2,$=7>$?8:11):(N=e.a[t].j,$=4>N?7>$?8:11:7>$?7:10),I=e.a[O],4>N?N?1==N?(e.x[0]=I.ac,e.x[1]=I.bc,e.x[2]=I.dc,e.x[3]=I.lc):2==N?(e.x[0]=I.dc,e.x[1]=I.bc,e.x[2]=I.ac,e.x[3]=I.lc):(e.x[0]=I.lc,e.x[1]=I.bc,e.x[2]=I.ac,e.x[3]=I.dc):(e.x[0]=I.bc,e.x[1]=I.ac,e.x[2]=I.dc,e.x[3]=I.lc):(e.x[0]=N-4,e.x[1]=I.bc,e.x[2]=I.ac,e.x[3]=I.dc)),e.a[t].Hc=$,e.a[t].bc=e.x[0],e.a[t].ac=e.x[1],e.a[t].dc=e.x[2],e.a[t].lc=e.x[3],_=e.a[t].z,a=C(e.b,-1),v=C(e.b,-e.x[0]-1-1),A=r&e.y,o=_+Yt[e.C[($<<4)+A]>>>2]+nt(Xr(e.A,r,C(e.b,-2)),$>=7,v,a),R=e.a[t+1],g=0,R.z>o&&(R.z=o,R.r=t,R.j=-1,R.t=0,g=1),B=_+Yt[2048-e.C[($<<4)+A]>>>2],Z=B+Yt[2048-e.bb[$]>>>2],v!=a||t>R.r&&!R.j||(V=Z+(Yt[e.hb[$]>>>2]+Yt[e._[($<<4)+A]>>>2]),R.z>=V&&(R.z=V,R.r=t,R.j=0,R.t=0,g=1)),w=F(e.b)+1,w=w>4095-t?4095-t:w,b=w,!(2>b)){if(b>e.n&&(b=e.n),!g&&v!=a&&(q=Math.min(w-1,e.n),P=z(e.b,0,e.x[0],q),P>=2)){for(K=U($),H=r+1&e.y,M=o+Yt[2048-e.C[(K<<4)+H]>>>2]+Yt[2048-e.bb[K]>>>2],y=t+1+P;y>m;)e.a[++m].z=268435455;s=M+(J=Jr(e.i,P-2,H),J+Cr(e,0,K,H)),x=e.a[y],x.z>s&&(x.z=s,x.r=t+1,x.j=0,x.t=1,x.Ac=0)}for(j=2,W=0;4>W;++W)if(h=z(e.b,-1,e.x[W],b),!(2>h)){l=h;do{for(;t+h>m;)e.a[++m].z=268435455;s=Z+(Q=Jr(e.i,h-2,A),Q+Cr(e,W,$,A)),x=e.a[t+h],x.z>s&&(x.z=s,x.r=t,x.j=W,x.t=0)}while(--h>=2);if(h=l,W||(j=h+1),w>h&&(q=Math.min(w-1-h,e.n),P=z(e.b,h,e.x[W],q),P>=2)){for(K=7>$?8:11,H=r+h&e.y,n=Z+(X=Jr(e.i,h-2,A),X+Cr(e,W,$,A))+Yt[e.C[(K<<4)+H]>>>2]+nt(Xr(e.A,r+h,C(e.b,h-1-1)),1,C(e.b,h-1-(e.x[W]+1)),C(e.b,h-1)),K=U(K),H=r+h+1&e.y,k=n+Yt[2048-e.C[(K<<4)+H]>>>2],M=k+Yt[2048-e.bb[K]>>>2],y=h+1+P;t+y>m;)e.a[++m].z=268435455;s=M+(er=Jr(e.i,P-2,H),er+Cr(e,0,K,H)),x=e.a[t+y],x.z>s&&(x.z=s,x.r=t+h+1,x.j=0,x.t=1,x.Ac=1,x.r2=t,x.j2=W)}}if(S>b){for(S=b,E=0;S>e.m[E];E+=2);e.m[E]=S,E+=2}if(S>=j){for(D=B+Yt[e.bb[$]>>>2];t+S>m;)e.a[++m].z=268435455;for(L=0;j>e.m[L];)L+=2;for(h=j;;++h)if(i=e.m[L+1],s=D+yr(e,i,h,A),x=e.a[t+h],x.z>s&&(x.z=s,x.r=t,x.j=i+4,x.t=0),h==e.m[L]){if(w>h&&(q=Math.min(w-1-h,e.n),P=z(e.b,h,i,q),P>=2)){for(K=7>$?7:10,H=r+h&e.y,n=s+Yt[e.C[(K<<4)+H]>>>2]+nt(Xr(e.A,r+h,C(e.b,h-1-1)),1,C(e.b,h-(i+1)-1),C(e.b,h-1)),K=U(K),H=r+h+1&e.y,k=n+Yt[2048-e.C[(K<<4)+H]>>>2],M=k+Yt[2048-e.bb[K]>>>2],y=h+1+P;t+y>m;)e.a[++m].z=268435455;s=M+(rr=Jr(e.i,P-2,H),rr+Cr(e,0,K,H)),x=e.a[t+y],x.z>s&&(x.z=s,x.r=t+h+1,x.j=0,x.t=1,x.Ac=1,x.r2=t,x.j2=i+4)}if(L+=2,L==E)break}}}}}function yr(e,r,t,o){var n,s=Q(t);return n=128>r?e.lb[128*s+r]:e.P[(s<<6)+Zr(r)]+e.nc[15&r],n+Jr(e.$,t-2,o)}function Cr(e,r,t,o){var n;return r?(n=Yt[2048-e.hb[t]>>>2],1==r?n+=Yt[e.Ub[t]>>>2]:(n+=Yt[2048-e.Ub[t]>>>2],n+=wt(e.vc[t],r-2))):(n=Yt[e.hb[t]>>>2],n+=Yt[2048-e._[(t<<4)+o]>>>2]),n}function zr(e,r,t){return Yt[e.hb[r]>>>2]+Yt[e._[(r<<4)+t]>>>2]}function Fr(e){kr(e),Dt(e.d),gt(e.C),gt(e._),gt(e.bb),gt(e.hb),gt(e.Ub),gt(e.vc),gt(e.Sb),et(e.A);for(var r=0;4>r;++r)gt(e.K[r].G);jr(e.$,1<0&&(Y(e.b,r),e.s+=r)}function xr(e){var r=0;return e.D=H(e.b,e.m),e.D>0&&(r=e.m[e.D-2],r==e.n&&(r+=z(e.b,r-1,e.m[e.D-1],273-r))),++e.s,r}function Nr(e){e.b&&e.W&&(e.b.cc=null,e.W=0)}function Or(e){Nr(e),e.d.Ab=null}function Ar(e,r){e.ab=r;for(var t=0;r>1<>24;for(var t=0;4>t;++t)e.fc[1+t]=e.ab>>8*t<<24>>24;k(r,e.fc,0,5)}function Wr(e,r){if(e.Gc){kt(e.d,e.C,(e.l<<4)+r,1),kt(e.d,e.bb,e.l,0),e.l=7>e.l?7:10,Kr(e.$,e.d,0,r);var t=Q(2);mt(e.K[t],e.d,63),Rt(e.d,67108863,26),pt(e.S,e.d,15)}}function Tr(e){return 2048>e?Zt[e]:2097152>e?Zt[e>>10]+20:Zt[e>>20]+40}function Zr(e){return 131072>e?Zt[e>>6]+12:134217728>e?Zt[e>>16]+32:Zt[e>>26]+52}function Yr(e,r,t,o){8>t?(kt(r,e.db,0,0),mt(e.Vb[o],r,t)):(t-=8,kt(r,e.db,0,1),8>t?(kt(r,e.db,1,0),mt(e.Wb[o],r,t)):(kt(r,e.db,1,1),mt(e.ic,r,t-8)))}function Vr(e){e.db=t(2),e.Vb=t(16),e.Wb=t(16),e.ic=ft({},8);for(var r=0;16>r;++r)e.Vb[r]=ft({},3),e.Wb[r]=ft({},3);return e}function jr(e,r){gt(e.db);for(var t=0;r>t;++t)gt(e.Vb[t].G),gt(e.Wb[t].G);gt(e.ic.G)}function $r(e,r,t,o,n){var s,i,_,a,c;for(s=Yt[e.db[0]>>>2],i=Yt[2048-e.db[0]>>>2],_=i+Yt[e.db[1]>>>2],a=i+Yt[2048-e.db[1]>>>2],c=0,c=0;8>c;++c){if(c>=t)return;o[n+c]=s+dt(e.Vb[r],c)}for(;16>c;++c){if(c>=t)return;o[n+c]=_+dt(e.Wb[r],c-8)}for(;t>c;++c)o[n+c]=a+dt(e.ic,c-8-8)}function Kr(e,r,t,o){Yr(e,r,t,o),0==--e.sc[o]&&($r(e,o,e.rb,e.Cc,272*o),e.sc[o]=e.rb)}function qr(e){return Vr(e),e.Cc=[],e.sc=[],e}function Jr(e,r,t){return e.Cc[272*t+r]}function Qr(e,r){for(var t=0;r>t;++t)$r(e,t,e.rb,e.Cc,272*t),e.sc[t]=e.rb}function Ur(e,r,o){var n,s;if(null==e.V||e.u!=o||e.I!=r)for(e.I=r,e.qc=(1<n;++n)e.V[n]=ot({})}function Xr(e,r,t){return e.V[((r&e.qc)<>>8-e.u)]}function et(e){var r,t=1<r;++r)gt(e.V[r].tb)}function rt(e,r,t){var o,n,s=1;for(n=7;n>=0;--n)o=t>>n&1,kt(r,e.tb,s,o),s=s<<1|o}function tt(e,r,t,o){var n,s,i,_,a=1,c=1;for(s=7;s>=0;--s)n=o>>s&1,_=c,a&&(i=t>>s&1,_+=1+i<<8,a=i==n),kt(r,e.tb,_,n),c=c<<1|n}function ot(e){return e.tb=t(768),e}function nt(e,r,t,o){var n,s,i=1,_=7,a=0;if(r)for(;_>=0;--_)if(s=t>>_&1,n=o>>_&1,a+=wt(e.tb[(1+s<<8)+i],n),i=i<<1|n,s!=n){--_;break}for(;_>=0;--_)n=o>>_&1,a+=wt(e.tb[i],n),i=i<<1|n;return a}function st(e){e.j=-1,e.t=0}function it(e){e.j=0,e.t=0}function _t(e,r){return e.F=r,e.G=t(1<o;++o)t=vt(r,e.G,n),n<<=1,n+=t,s|=t<s;++s)n=vt(t,e,r+i),i<<=1,i+=n,_|=n<>>n&1,kt(r,e.G,s,o),s=s<<1|o}function dt(e,r){var t,o,n=1,s=0;for(o=e.F;0!=o;)--o,t=r>>>o&1,s+=wt(e.G[n],t),n=(n<<1)+t;return s}function pt(e,r,t){var o,n,s=1;for(n=0;e.F>n;++n)o=1&t,kt(r,e.G,s,o),s=s<<1|o,t>>=1}function ht(e,r){var t,o,n=1,s=0;for(o=e.F;0!=o;--o)t=1&r,r>>>=1,s+=wt(e.G[n],t),n=n<<1|t;return s}function Pt(e,r,t,o,n){var s,i,_=1;for(i=0;o>i;++i)s=1&n,kt(t,e,r+_,s),_=_<<1|s,n>>=1}function lt(e,r,t,o){var n,s,i=1,_=0;for(s=t;0!=s;--s)n=1&o,o>>>=1,_+=Yt[(2047&(e[r+i]-n^-n))>>>2],i=i<<1|n;return _}function vt(e,r,t){var o,n=r[t];return o=(e.E>>>11)*n,(-2147483648^o)>(-2147483648^e.Bb)?(e.E=o,r[t]=n+(2048-n>>>5)<<16>>16,-16777216&e.E||(e.Bb=e.Bb<<8|l(e.Ab),e.E<<=8),0):(e.E-=o,e.Bb-=o,r[t]=n-(n>>>5)<<16>>16,-16777216&e.E||(e.Bb=e.Bb<<8|l(e.Ab),e.E<<=8),1)}function Bt(e,r){var t,o,n=0;for(t=r;0!=t;--t)e.E>>>=1,o=e.Bb-e.E>>>31,e.Bb-=e.E&o-1,n=n<<1|1-o,-16777216&e.E||(e.Bb=e.Bb<<8|l(e.Ab),e.E<<=8);return n}function St(e){e.Bb=0,e.E=-1;for(var r=0;5>r;++r)e.Bb=e.Bb<<8|l(e.Ab)}function gt(e){for(var r=e.length-1;r>=0;--r)e[r]=1024}function kt(e,r,t,s){var i,_=r[t];i=(e.E>>>11)*_,s?(e.xc=o(e.xc,n(a(i),[4294967295,0])),e.E-=i,r[t]=_-(_>>>5)<<16>>16):(e.E=i,r[t]=_+(2048-_>>>5)<<16>>16),-16777216&e.E||(e.E<<=8,bt(e))}function Rt(e,r,t){for(var n=t-1;n>=0;--n)e.E>>>=1,1==(r>>>n&1)&&(e.xc=o(e.xc,a(e.E))),-16777216&e.E||(e.E<<=8,bt(e))}function Mt(e){return o(o(a(e.Jb),e.mc),[4,0])}function Dt(e){e.mc=Gt,e.xc=Gt,e.E=-1,e.Jb=1,e.Oc=0}function bt(e){var r,t=c(p(e.xc,32));if(0!=t||s(e.xc,[4278190080,0])<0){e.mc=o(e.mc,a(e.Jb)),r=e.Oc;do g(e.Ab,r+t),r=255;while(0!=--e.Jb);e.Oc=c(e.xc)>>>24}++e.Jb,e.xc=m(n(e.xc,[16777215,0]),8)}function wt(e,r){return Yt[(2047&(e-r^-r))>>>2]}function Et(e){for(var r,t,o,n=0,s=0,i=e.length,_=[],a=[];i>n;++n,++s){if(r=255&e[n],128&r)if(192==(224&r)){if(n+1>=i)return e;if(t=255&e[++n],128!=(192&t))return e;a[s]=(31&r)<<6|63&t}else{if(224!=(240&r))return e;
+if(n+2>=i)return e;if(t=255&e[++n],128!=(192&t))return e;if(o=255&e[++n],128!=(192&o))return e;a[s]=(15&r)<<12|(63&t)<<6|63&o}else{if(!r)return e;a[s]=r}16383==s&&(_.push(String.fromCharCode.apply(String,a)),s=-1)}return s>0&&(a.length=s,_.push(String.fromCharCode.apply(String,a))),_.join("")}function Lt(e){var r,t,o,n=[],s=0,i=e.length;if("object"==typeof e)return e;for(R(e,0,i,n,0),o=0;i>o;++o)r=n[o],r>=1&&127>=r?++s:s+=!r||r>=128&&2047>=r?2:3;for(t=[],s=0,o=0;i>o;++o)r=n[o],r>=1&&127>=r?t[s++]=r<<24>>24:!r||r>=128&&2047>=r?(t[s++]=(192|r>>6&31)<<24>>24,t[s++]=(128|63&r)<<24>>24):(t[s++]=(224|r>>12&15)<<24>>24,t[s++]=(128|r>>6&63)<<24>>24,t[s++]=(128|63&r)<<24>>24);return t}function yt(e){return e[1]+e[0]}function Ct(e,t,o,n){function s(){try{for(var e,r=(new Date).getTime();rr(a.c.yb);)if(i=yt(a.c.yb.Pb)/yt(a.c.Tb),(new Date).getTime()-r>200)return n(i),Nt(s,0),0;n(1),e=S(a.c.Nb),Nt(o.bind(null,e),0)}catch(t){o(null,t)}}var i,_,a={},c=void 0===o&&void 0===n;if("function"!=typeof o&&(_=o,o=n=0),n=n||function(e){return void 0!==_?r(e,_):void 0},o=o||function(e,r){return void 0!==_?postMessage({action:Ft,cbn:_,result:e,error:r}):void 0},c){for(a.c=w({},Lt(e),Vt(t));rr(a.c.yb););return S(a.c.Nb)}try{a.c=w({},Lt(e),Vt(t)),n(0)}catch(u){return o(null,u)}Nt(s,0)}function zt(e,t,o){function n(){try{for(var e,r=0,i=(new Date).getTime();rr(c.d.yb);)if(++r%1e3==0&&(new Date).getTime()-i>200)return _&&(s=yt(c.d.yb.Z.g)/a,o(s)),Nt(n,0),0;o(1),e=Et(S(c.d.Nb)),Nt(t.bind(null,e),0)}catch(u){t(null,u)}}var s,i,_,a,c={},u=void 0===t&&void 0===o;if("function"!=typeof t&&(i=t,t=o=0),o=o||function(e){return void 0!==i?r(_?e:-1,i):void 0},t=t||function(e,r){return void 0!==i?postMessage({action:It,cbn:i,result:e,error:r}):void 0},u){for(c.d=L({},e);rr(c.d.yb););return Et(S(c.d.Nb))}try{c.d=L({},e),a=yt(c.d.Tb),_=a>-1,o(0)}catch(f){return t(null,f)}Nt(n,0)}var Ft=1,It=2,xt=3,Nt="function"==typeof setImmediate?setImmediate:setTimeout,Ot=4294967296,At=[4294967295,-Ot],Ht=[0,-0x8000000000000000],Gt=[0,0],Wt=[1,0],Tt=function(){var e,r,t,o=[];for(e=0;256>e;++e){for(t=e,r=0;8>r;++r)0!=(1&t)?t=t>>>1^-306674912:t>>>=1;o[e]=t}return o}(),Zt=function(){var e,r,t,o=2,n=[0,1];for(t=2;22>t;++t)for(r=1<<(t>>1)-1,e=0;r>e;++e,++o)n[o]=t<<24>>24;return n}(),Yt=function(){var e,r,t,o,n=[];for(r=8;r>=0;--r)for(o=1<<9-r-1,e=1<<9-r,t=o;e>t;++t)n[t]=(r<<6)+(e-t<<6>>>9-r-1);return n}(),Vt=function(){var e=[{s:16,f:64,m:0},{s:20,f:64,m:0},{s:19,f:64,m:1},{s:20,f:64,m:1},{s:21,f:128,m:1},{s:22,f:128,m:1},{s:23,f:128,m:1},{s:24,f:255,m:1},{s:25,f:255,m:1}];return function(r){return e[r-1]||e[6]}}();return"undefined"==typeof onmessage||"undefined"!=typeof window&&void 0!==window.document||!function(){onmessage=function(r){r&&r.gc&&(r.gc.action==It?e.decompress(r.gc.gc,r.gc.cbn):r.gc.action==Ft&&e.compress(r.gc.gc,r.gc.Rc,r.gc.cbn))}}(),{compress:Ct,decompress:zt}}();this.LZMA=this.LZMA_WORKER=e;
\ No newline at end of file
diff --git a/haxelib.json b/haxelib.json
index 9041ebbbaa..cd140378b8 100644
--- a/haxelib.json
+++ b/haxelib.json
@@ -4,8 +4,8 @@
"license": "MIT",
"tags": [],
"description": "A foundational Haxe framework for cross-platform development",
- "version": "8.2.0",
- "releasenote": "Update HashLink, iOS and Android build fixes, and ongoing improvements",
+ "version": "8.3.0",
+ "releasenote": "Various bug fixes",
"contributors": [
"singmajesty",
"bowlerhat",
diff --git a/include.xml b/include.xml
index 0763751cb8..52b1cb9015 100644
--- a/include.xml
+++ b/include.xml
@@ -91,7 +91,8 @@
-
+
+
@@ -218,4 +219,4 @@
-
\ No newline at end of file
+
diff --git a/ndll/MacArm64/.gitignore b/ndll/MacArm64/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/project/lib/custom/jpeg/jconfig.h b/project/lib/custom/jpeg/jconfig.h
index 0ffd630dde..a1c4214330 100644
--- a/project/lib/custom/jpeg/jconfig.h
+++ b/project/lib/custom/jpeg/jconfig.h
@@ -19,7 +19,9 @@
#define MEM_SRCDST_SUPPORTED 1
/* Use accelerated SIMD routines. */
+#ifndef __ANDROID__
#define WITH_SIMD 1
+#endif
/*
* Define BITS_IN_JSAMPLE as either
@@ -67,9 +69,10 @@
#endif
/* If rpcndr.h has defined boolean, jmorecfg.h should not. */
-#ifdef __RPCNDR_H__
-#define HAVE_BOOLEAN
+#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */
+typedef unsigned char boolean;
#endif
+#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
diff --git a/project/lib/custom/openal/include/config.h b/project/lib/custom/openal/include/config.h
index d8f843244f..89959b23e3 100644
--- a/project/lib/custom/openal/include/config.h
+++ b/project/lib/custom/openal/include/config.h
@@ -1,4 +1,8 @@
-#ifdef HX_MACOS
+#if defined(HX_MACOS) && defined(HXCPP_ARM64)
+
+#include "config-macos-arm64.h"
+
+#elif defined(HX_MACOS)
#include "config-macos-x86_64.h"
diff --git a/project/lib/custom/pixman/config.h b/project/lib/custom/pixman/config.h
index 695e0428c1..c2829330bd 100644
--- a/project/lib/custom/pixman/config.h
+++ b/project/lib/custom/pixman/config.h
@@ -293,14 +293,14 @@
/* #undef USE_OPENMP */
/* use SSE2 compiler intrinsics */
-#if defined(HX_WINDOWS) || defined(HX_MACOS) || (defined(HX_LINUX) && !defined(RASPBERRYPI))
+#if defined(HX_WINDOWS) || (defined(HX_MACOS) && !defined(HXCPP_ARM64)) || (defined(HX_LINUX) && !defined(RASPBERRYPI))
#define USE_SSE2 1
#else
/* #undef USE_SSE2 */
#endif
/* use SSSE3 compiler intrinsics */
-#if defined(HX_WINDOWS) || defined(HX_MACOS) || (defined(HX_LINUX) && !defined(RASPBERRYPI))
+#if defined(HX_WINDOWS) || (defined(HX_MACOS) && !defined(HXCPP_ARM64)) || (defined(HX_LINUX) && !defined(RASPBERRYPI))
#define USE_SSE3 1
#else
/* #undef USE_SSE3 */
diff --git a/project/lib/openal-files.xml b/project/lib/openal-files.xml
index b68dc17686..375151d5cb 100644
--- a/project/lib/openal-files.xml
+++ b/project/lib/openal-files.xml
@@ -152,11 +152,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/project/lib/pixman-files.xml b/project/lib/pixman-files.xml
index 7f8da069cc..01fb83b21b 100644
--- a/project/lib/pixman-files.xml
+++ b/project/lib/pixman-files.xml
@@ -23,11 +23,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -57,15 +57,15 @@
-
+
-
-
+
+
diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp
index fc1fdf1b41..62d89441cf 100644
--- a/project/src/ExternalInterface.cpp
+++ b/project/src/ExternalInterface.cpp
@@ -52,6 +52,7 @@
#include
#include
#endif
+#include
#include
#include
@@ -134,36 +135,82 @@ namespace lime {
}
- std::wstring* hxstring_to_wstring (HxString val) {
+ std::string wstring_utf8 (const std::wstring& val) {
- if (val.c_str ()) {
+ std::string out;
+ unsigned int codepoint = 0;
- std::string _val = std::string (val.c_str ());
- #ifdef HX_WINDOWS
- std::wstring_convert> converter;
- return new std::wstring (converter.from_bytes (_val));
- #else
- return new std::wstring (_val.begin (), _val.end ());
- #endif
+ for (const wchar_t chr : val) {
- } else {
+ if (chr >= 0xd800 && chr <= 0xdbff) {
- return 0;
+ codepoint = ((chr - 0xd800) << 10) + 0x10000;
+
+ } else {
+
+ if (chr >= 0xdc00 && chr <= 0xdfff) {
+
+ codepoint |= chr - 0xdc00;
+
+ } else {
+
+ codepoint = chr;
+
+ }
+
+ if (codepoint <= 0x7f) {
+
+ out.append (1, static_cast (codepoint));
+
+ } else if (codepoint <= 0x7ff) {
+
+ out.append (1, static_cast (0xc0 | ((codepoint >> 6) & 0x1f)));
+ out.append (1, static_cast (0x80 | (codepoint & 0x3f)));
+
+ } else if (codepoint <= 0xffff) {
+
+ out.append (1, static_cast (0xe0 | ((codepoint >> 12) & 0x0f)));
+ out.append (1, static_cast (0x80 | ((codepoint >> 6) & 0x3f)));
+ out.append (1, static_cast (0x80 | (codepoint & 0x3f)));
+
+ } else {
+
+ out.append (1, static_cast (0xf0 | ((codepoint >> 18) & 0x07)));
+ out.append (1, static_cast (0x80 | ((codepoint >> 12) & 0x3f)));
+ out.append (1, static_cast (0x80 | ((codepoint >> 6) & 0x3f)));
+ out.append (1, static_cast (0x80 | (codepoint & 0x3f)));
+
+ }
+
+ codepoint = 0;
+
+ }
}
+ return out;
+
}
- std::wstring* hxstring_to_wstring (hl_vstring* val) {
+ vbyte* hl_wstring_to_utf8_bytes (const std::wstring& val) {
- if (val) {
+ const std::string utf8 (wstring_utf8 (val));
+ vbyte* const bytes = hl_alloc_bytes (utf8.size () + 1);
+ std::memcpy(bytes, utf8.c_str (), utf8.size () + 1);
+ return bytes;
+
+ }
+
+
+ std::wstring* hxstring_to_wstring (HxString val) {
+
+ if (val.c_str ()) {
- std::string _val = std::string (hl_to_utf8 (val->bytes));
#ifdef HX_WINDOWS
- std::wstring_convert> converter;
- return new std::wstring (converter.from_bytes (_val));
+ return new std::wstring (hxs_wchar (val, nullptr));
#else
+ const std::string _val (hxs_utf8 (val, nullptr));
return new std::wstring (_val.begin (), _val.end ());
#endif
@@ -176,15 +223,16 @@ namespace lime {
}
- value wstring_to_value (std::wstring* val) {
+ std::wstring* hxstring_to_wstring (hl_vstring* val) {
if (val) {
+ std::string _val = std::string (hl_to_utf8 (val->bytes));
#ifdef HX_WINDOWS
- return alloc_wstring (val->c_str ());
+ std::wstring_convert> converter;
+ return new std::wstring (converter.from_bytes (_val));
#else
- std::string _val = std::string (val->begin (), val->end ());
- return alloc_string (_val.c_str ());
+ return new std::wstring (_val.begin (), _val.end ());
#endif
} else {
@@ -196,23 +244,15 @@ namespace lime {
}
- vbyte* wstring_to_vbytes (std::wstring* val) {
+ value wstring_to_value (std::wstring* val) {
if (val) {
#ifdef HX_WINDOWS
- int size = std::wcslen (val->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, val->c_str (), size);
- result[size] = '\0';
- return (vbyte*)result;
+ return alloc_wstring (val->c_str ());
#else
std::string _val = std::string (val->begin (), val->end ());
- int size = std::strlen (_val.c_str ());
- char* result = (char*)malloc (size + 1);
- std::strncpy (result, _val.c_str (), size);
- result[size] = '\0';
- return (vbyte*)result;
+ return alloc_string (_val.c_str ());
#endif
} else {
@@ -522,7 +562,7 @@ namespace lime {
value lime_bytes_read_file (HxString path, value bytes) {
Bytes data (bytes);
- data.ReadFile (path.c_str ());
+ data.ReadFile (hxs_utf8 (path, nullptr));
return data.Value (bytes);
}
@@ -622,7 +662,7 @@ namespace lime {
void lime_clipboard_set_text (HxString text) {
- Clipboard::SetText (text.c_str ());
+ Clipboard::SetText (hxs_utf8 (text, nullptr));
}
@@ -769,9 +809,9 @@ namespace lime {
if (path) {
- vbyte* _path = wstring_to_vbytes (path);
+ vbyte* const result = hl_wstring_to_utf8_bytes (*path);
delete path;
- return _path;
+ return result;
} else {
@@ -835,9 +875,9 @@ namespace lime {
if (path) {
- vbyte* _path = wstring_to_vbytes (path);
+ vbyte* const result = hl_wstring_to_utf8_bytes (*path);
delete path;
- return _path;
+ return result;
} else {
@@ -906,8 +946,7 @@ namespace lime {
for (int i = 0; i < files.size (); i++) {
- vbyte* _file = wstring_to_vbytes (files[i]);
- *resultData++ = _file;
+ *resultData++ = hl_wstring_to_utf8_bytes (*files[i]);
delete files[i];
}
@@ -970,9 +1009,9 @@ namespace lime {
if (path) {
- vbyte* _path = wstring_to_vbytes (path);
+ vbyte* const result = hl_wstring_to_utf8_bytes (*path);
delete path;
- return _path;
+ return result;
} else {
@@ -1143,12 +1182,11 @@ namespace lime {
#ifdef LIME_FREETYPE
Font *font = (Font*)fontHandle->ptr;
wchar_t *name = font->GetFamilyName ();
- int size = std::wcslen (name);
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, name, size);
- result[size] = '\0';
+ if (!name)
+ return nullptr;
+ vbyte* const result = hl_wstring_to_utf8_bytes (name);
delete name;
- return (vbyte*)result;
+ return result;
#else
return 0;
#endif
@@ -1160,7 +1198,7 @@ namespace lime {
#ifdef LIME_FREETYPE
Font *font = (Font*)val_data (fontHandle);
- return font->GetGlyphIndex ((char*)character.c_str ());
+ return font->GetGlyphIndex (hxs_utf8 (character, nullptr));
#else
return -1;
#endif
@@ -1184,7 +1222,7 @@ namespace lime {
#ifdef LIME_FREETYPE
Font *font = (Font*)val_data (fontHandle);
- return (value)font->GetGlyphIndices (true, (char*)characters.c_str ());
+ return (value)font->GetGlyphIndices (true, hxs_utf8 (characters, nullptr));
#else
return alloc_null ();
#endif
@@ -2347,7 +2385,7 @@ namespace lime {
value lime_jpeg_decode_file (HxString path, bool decodeData, value buffer) {
ImageBuffer imageBuffer (buffer);
- Resource resource = Resource (path.c_str ());
+ Resource resource = Resource (hxs_utf8 (path, nullptr));
#ifdef LIME_JPEG
if (JPEG::Decode (&resource, &imageBuffer, decodeData)) {
@@ -2585,7 +2623,7 @@ namespace lime {
value lime_png_decode_file (HxString path, bool decodeData, value buffer) {
ImageBuffer imageBuffer (buffer);
- Resource resource = Resource (path.c_str ());
+ Resource resource = Resource (hxs_utf8 (path, nullptr));
#ifdef LIME_PNG
if (PNG::Decode (&resource, &imageBuffer, decodeData)) {
@@ -2690,13 +2728,9 @@ namespace lime {
if (model) {
- int size = std::wcslen (model->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, model->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*model);
delete model;
-
- return (vbyte*)result;
+ return result;
}
@@ -2734,13 +2768,9 @@ namespace lime {
if (vendor) {
- int size = std::wcslen (vendor->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, vendor->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*vendor);
delete vendor;
-
- return (vbyte*)result;
+ return result;
}
@@ -2753,7 +2783,7 @@ namespace lime {
value lime_system_get_directory (int type, HxString company, HxString title) {
- std::wstring* path = System::GetDirectory ((SystemDirectory)type, company.c_str (), title.c_str ());
+ std::wstring* path = System::GetDirectory ((SystemDirectory)type, hxs_utf8 (company, nullptr), hxs_utf8 (title, nullptr));
if (path) {
@@ -2778,13 +2808,9 @@ namespace lime {
if (path) {
- int size = std::wcslen (path->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, path->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*path);
delete path;
-
- return (vbyte*)result;
+ return result;
}
@@ -2872,13 +2898,9 @@ namespace lime {
if (label) {
- int size = std::wcslen (label->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, label->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*label);
delete label;
-
- return (vbyte*)result;
+ return result;
}
@@ -2916,13 +2938,9 @@ namespace lime {
if (name) {
- int size = std::wcslen (name->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, name->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*name);
delete name;
-
- return (vbyte*)result;
+ return result;
}
@@ -2960,14 +2978,9 @@ namespace lime {
if (version) {
- int size = std::wcslen (version->c_str ());
- char* result = (char*)malloc (size + 1);
- std::wcstombs (result, version->c_str (), size);
- result[size] = '\0';
+ vbyte* const result = hl_wstring_to_utf8_bytes (*version);
delete version;
-
- return (vbyte*)result;
-
+ return result;
}
#endif
@@ -3120,7 +3133,7 @@ namespace lime {
void lime_window_alert (value window, HxString message, HxString title) {
Window* targetWindow = (Window*)val_data (window);
- targetWindow->Alert (message.c_str (), title.c_str ());
+ targetWindow->Alert (hxs_utf8 (message, nullptr), hxs_utf8 (title, nullptr));
}
@@ -3128,8 +3141,8 @@ namespace lime {
HL_PRIM void HL_NAME(hl_window_alert) (HL_CFFIPointer* window, hl_vstring* message, hl_vstring* title) {
Window* targetWindow = (Window*)window->ptr;
- const char *cmessage = message ? hl_to_utf8(message->bytes) : NULL;
- const char *ctitle = title ? hl_to_utf8(title->bytes) : NULL;
+ const char *cmessage = message ? hl_to_utf8(message->bytes) : nullptr;
+ const char *ctitle = title ? hl_to_utf8(title->bytes) : nullptr;
targetWindow->Alert (cmessage, ctitle);
}
@@ -3209,7 +3222,7 @@ namespace lime {
value lime_window_create (value application, int width, int height, int flags, HxString title) {
- Window* window = CreateWindow ((Application*)val_data (application), width, height, flags, title.c_str ());
+ Window* window = CreateWindow ((Application*)val_data (application), width, height, flags, hxs_utf8 (title, nullptr));
return CFFIPointer (window, gc_window);
}
@@ -3774,13 +3787,14 @@ namespace lime {
value lime_window_set_title (value window, HxString title) {
Window* targetWindow = (Window*)val_data (window);
- const char* result = targetWindow->SetTitle (title.c_str ());
+ const char* titleUtf8 = hxs_utf8 (title, nullptr);
+ const char* result = targetWindow->SetTitle (titleUtf8);
if (result) {
value _result = alloc_string (result);
- if (result != title.c_str ()) {
+ if (result != titleUtf8) {
free ((char*) result);
diff --git a/project/src/graphics/format/JPEG.cpp b/project/src/graphics/format/JPEG.cpp
index 3917d8bb55..9253e45aae 100644
--- a/project/src/graphics/format/JPEG.cpp
+++ b/project/src/graphics/format/JPEG.cpp
@@ -25,9 +25,19 @@ namespace lime {
static void OnOutput (j_common_ptr cinfo) {}
+ // #ifdef NDEBUG
+ // char errorMessage[JMSG_LENGTH_MAX];
+ // #endif
+
static void OnError (j_common_ptr cinfo) {
ErrorData * err = (ErrorData *)cinfo->err;
+
+ // #ifdef NDEBUG
+ // ( *(cinfo->err->format_message) ) (cinfo, errorMessage);
+ // fprintf(stderr, "%s\n", errorMessage);
+ // #endif
+
longjmp (err->on_error, 1);
}
diff --git a/project/src/math/Matrix3.cpp b/project/src/math/Matrix3.cpp
index 67eef50177..c55586d2ad 100644
--- a/project/src/math/Matrix3.cpp
+++ b/project/src/math/Matrix3.cpp
@@ -67,6 +67,18 @@ namespace lime {
value Matrix3::Value (value matrix3) {
+ if (!init) {
+
+ id_a = val_id ("a");
+ id_b = val_id ("b");
+ id_c = val_id ("c");
+ id_d = val_id ("d");
+ id_tx = val_id ("tx");
+ id_ty = val_id ("ty");
+ init = true;
+
+ }
+
alloc_field (matrix3, id_a, alloc_float (a));
alloc_field (matrix3, id_b, alloc_float (b));
alloc_field (matrix3, id_c, alloc_float (c));
diff --git a/run.n b/run.n
index 2544816fbf..2789ab0ca7 100644
Binary files a/run.n and b/run.n differ
diff --git a/src/lime/_internal/backend/html5/HTML5Thread.hx b/src/lime/_internal/backend/html5/HTML5Thread.hx
index 845a2924d7..022a085da1 100644
--- a/src/lime/_internal/backend/html5/HTML5Thread.hx
+++ b/src/lime/_internal/backend/html5/HTML5Thread.hx
@@ -477,7 +477,7 @@ abstract Message(Dynamic) from Dynamic to Dynamic
// Skip `null` for obvious reasons.
return object == null
// No need to preserve a primitive type.
- || !#if (haxe_ver >= 4.2) Std.isOfType #else untyped __js__ #end (object, Object)
+ || !#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (object, Object)
// Objects with this field have been deliberately excluded.
|| Reflect.field(object, SKIP_FIELD) == true
// A `Uint8Array` (the type used by `haxe.io.Bytes`) can have
diff --git a/src/lime/_internal/backend/html5/HTML5Window.hx b/src/lime/_internal/backend/html5/HTML5Window.hx
index a38e4399f3..61c3901ea9 100644
--- a/src/lime/_internal/backend/html5/HTML5Window.hx
+++ b/src/lime/_internal/backend/html5/HTML5Window.hx
@@ -79,6 +79,8 @@ class HTML5Window
private var __focusPending:Bool;
+ private var __stopMousePropagation = false;
+
public function new(parent:Window)
{
this.parent = parent;
@@ -329,7 +331,7 @@ class HTML5Window
depth: Reflect.hasField(contextAttributes, "depth") ? contextAttributes.depth : true,
premultipliedAlpha: true,
stencil: Reflect.hasField(contextAttributes, "stencil") ? contextAttributes.stencil : false,
- preserveDrawingBuffer: false,
+ preserveDrawingBuffer: Reflect.hasField(contextAttributes, "preserveDrawingBuffer") ? contextAttributes.preserveDrawingBuffer : false,
failIfMajorPerformanceCaveat: false
};
@@ -661,9 +663,21 @@ class HTML5Window
case "mousedown":
if (event.currentTarget == parent.element)
{
- // Release outside browser window
+ // while the mouse button is down, and the mouse has
+ // moved outside the bounds of the parent element, we
+ // want both onMouseMove and onMouseUp to continue to be
+ // dispatched. otherwise, dragging objects around with
+ // the mouse will appear broken.
+ // however, if the mouse button isn't down, and the
+ // mouse is outside the bounds of the parent element,
+ // then onMouseMove and onMouseUp don't need to be
+ // dispatched.
+ // Flash embedded in HTML worked similarly.
Browser.window.addEventListener("mouseup", handleMouseEvent);
+ Browser.window.addEventListener("mousemove", handleMouseEvent);
}
+ // just to be safe, clear the flag on every mouse down
+ __stopMousePropagation = false;
parent.clickCount = event.detail;
parent.onMouseDown.dispatch(x, y, event.button);
@@ -697,13 +711,20 @@ class HTML5Window
}
case "mouseup":
- Browser.window.removeEventListener("mouseup", handleMouseEvent);
- if (event.currentTarget == parent.element)
+ // see comment below for mousemove for an explanation of
+ // what the __stopMousePropagation flag is used for.
+ if (__stopMousePropagation && event.currentTarget != parent.element)
{
- event.stopPropagation();
+ __stopMousePropagation = false;
+ return;
}
+ Browser.window.removeEventListener("mouseup", handleMouseEvent);
+ Browser.window.removeEventListener("mousemove", handleMouseEvent);
+
+ __stopMousePropagation = event.currentTarget == parent.element;
+
parent.clickCount = event.detail;
parent.onMouseUp.dispatch(x, y, event.button);
parent.clickCount = 0;
@@ -714,6 +735,46 @@ class HTML5Window
}
case "mousemove":
+
+ // this same listener is added to the parent element and to
+ // the browser window for both the mousemove and the mouseup
+ // event types, if mousedown happens first. this allows both
+ // onMouseMove and onMouseUp to be dispatched if the mouse
+ // moves outside the bounds of the parent element.
+
+ // since browser mouse events bubble, this listener will be
+ // called for the parent element first, as long as the mouse
+ // is still over the parent element. in that case, when the
+ // listener is called for the browser window, it should
+ // return early so that onMouseMove or onMouseUp isn't
+ // dispatched twice. this is done by checking the
+ // __stopMousePropagation flag when the current target isn't
+ // the parent element.
+
+ // however, if the mouse isn't over the parent element, the
+ // listener will be called only for the browser window, and
+ // not the parent element. in that case, it can proceed to
+ // dispatch either onMouseMove or onMouseUp, since this
+ // listener was called only once.
+
+ // again, this applies only if the mouse button is down. if
+ // the mouse button isn't down, then the listener won't be
+ // added to the browser window, and event won't be
+ // dispatched outside the bounds of the parent element.
+
+ if (__stopMousePropagation && event.currentTarget != parent.element)
+ {
+ // why not call event.stopPropagation() here? well,
+ // other JS code in the page may still be interested in
+ // the event. listening for the same events on both the
+ // parent element and on the browser window is just an
+ // implementation detail and shouldn't affect other
+ // listeners.
+ __stopMousePropagation = false;
+ return;
+ }
+ __stopMousePropagation = event.currentTarget == parent.element;
+
if (x != cacheMouseX || y != cacheMouseY)
{
parent.onMouseMove.dispatch(x, y);
diff --git a/src/lime/_internal/backend/native/NativeCFFI.hx b/src/lime/_internal/backend/native/NativeCFFI.hx
index 9f54e2478c..73ee74b378 100644
--- a/src/lime/_internal/backend/native/NativeCFFI.hx
+++ b/src/lime/_internal/backend/native/NativeCFFI.hx
@@ -3024,7 +3024,7 @@ class NativeCFFI
return 0;
}
- @:hlNative("lime", "hl_cairo_get_matrix") private static function lime_cairo_get_matrix(handle:CFFIPointer, out:Matrix3):Matrix3
+ @:hlNative("lime", "hl_cairo_get_matrix") private static function lime_cairo_get_matrix(handle:CFFIPointer, out:CairoMatrix3):CairoMatrix3
{
return null;
}
@@ -3141,7 +3141,7 @@ class NativeCFFI
@:hlNative("lime", "hl_cairo_set_line_width") private static function lime_cairo_set_line_width(handle:CFFIPointer, width:Float):Void {}
- @:hlNative("lime", "hl_cairo_set_matrix") private static function lime_cairo_set_matrix(handle:CFFIPointer, matrix:Matrix3):Void {}
+ @:hlNative("lime", "hl_cairo_set_matrix") private static function lime_cairo_set_matrix(handle:CFFIPointer, matrix:CairoMatrix3):Void {}
@:hlNative("lime", "hl_cairo_set_miter_limit") private static function lime_cairo_set_miter_limit(handle:CFFIPointer, miterLimit:Float):Void {}
@@ -3179,7 +3179,7 @@ class NativeCFFI
@:hlNative("lime", "hl_cairo_text_path") private static function lime_cairo_text_path(handle:CFFIPointer, text:String):Void {}
- @:hlNative("lime", "hl_cairo_transform") private static function lime_cairo_transform(handle:CFFIPointer, matrix:Matrix3):Void {}
+ @:hlNative("lime", "hl_cairo_transform") private static function lime_cairo_transform(handle:CFFIPointer, matrix:CairoMatrix3):Void {}
@:hlNative("lime", "hl_cairo_translate") private static function lime_cairo_translate(handle:CFFIPointer, x:Float, y:Float):Void {}
@@ -3325,7 +3325,7 @@ class NativeCFFI
return 0;
}
- @:hlNative("lime", "hl_cairo_pattern_get_matrix") private static function lime_cairo_pattern_get_matrix(handle:CFFIPointer, out:Matrix3):Matrix3
+ @:hlNative("lime", "hl_cairo_pattern_get_matrix") private static function lime_cairo_pattern_get_matrix(handle:CFFIPointer, out:CairoMatrix3):CairoMatrix3
{
return null;
}
@@ -3334,7 +3334,7 @@ class NativeCFFI
@:hlNative("lime", "hl_cairo_pattern_set_filter") private static function lime_cairo_pattern_set_filter(handle:CFFIPointer, filter:Int):Void {}
- @:hlNative("lime", "hl_cairo_pattern_set_matrix") private static function lime_cairo_pattern_set_matrix(handle:CFFIPointer, matrix:Matrix3):Void {}
+ @:hlNative("lime", "hl_cairo_pattern_set_matrix") private static function lime_cairo_pattern_set_matrix(handle:CFFIPointer, matrix:CairoMatrix3):Void {}
@:hlNative("lime", "hl_cairo_surface_flush") private static function lime_cairo_surface_flush(surface:CFFIPointer):Void {}
#end
diff --git a/src/lime/_internal/format/LZMA.hx b/src/lime/_internal/format/LZMA.hx
index 8604efad35..f54f2ae7cf 100644
--- a/src/lime/_internal/format/LZMA.hx
+++ b/src/lime/_internal/format/LZMA.hx
@@ -2,6 +2,7 @@ package lime._internal.format;
import haxe.io.Bytes;
import lime._internal.backend.native.NativeCFFI;
+import lime.utils.UInt8Array;
#if flash
import flash.utils.CompressionAlgorithm;
import flash.utils.ByteArray;
@@ -24,6 +25,16 @@ class LZMA
if (data == null) return null;
return @:privateAccess new Bytes(data.length, data.b);
#end
+ #elseif js
+ var data = untyped #if haxe4 js.Syntax.code #else __js__ #end ("LZMA.compress")(new UInt8Array(bytes.getData()), 5);
+ if (data is String)
+ {
+ return Bytes.ofString(data);
+ }
+ else
+ {
+ return Bytes.ofData(cast data);
+ }
#elseif flash
var byteArray:ByteArray = cast bytes.getData();
@@ -47,6 +58,16 @@ class LZMA
if (data == null) return null;
return @:privateAccess new Bytes(data.length, data.b);
#end
+ #elseif js
+ var data = untyped #if haxe4 js.Syntax.code #else __js__ #end ("LZMA.decompress")(new UInt8Array(bytes.getData()));
+ if (data is String)
+ {
+ return Bytes.ofString(data);
+ }
+ else
+ {
+ return Bytes.ofData(cast data);
+ }
#elseif flash
var byteArray:ByteArray = cast bytes.getData();
diff --git a/src/lime/app/Future.hx b/src/lime/app/Future.hx
index 2af97182d4..64f36c5200 100644
--- a/src/lime/app/Future.hx
+++ b/src/lime/app/Future.hx
@@ -67,24 +67,36 @@ import lime.utils.Log;
@:noCompletion private var __progressListeners:ArrayInt->Void>;
/**
- @param work Deprecated; use `Future.withEventualValue()` instead.
- @param useThreads Deprecated; use `Future.withEventualValue()` instead.
+ @param work Optional: a function to compute this future's value.
+ @param useThreads Whether to run `work` on a background thread, where supported.
+ If false or if this isn't a system target, it will run immediately on the main thread.
**/
- public function new(work:WorkFunctionT> = null, useThreads:Bool = false)
+ public function new(work:Void->T = null, useThreads:Bool = false)
{
if (work != null)
{
- var promise = new Promise();
- promise.future = this;
-
- #if (lime_threads && html5)
+ #if (lime_threads && !html5)
if (useThreads)
{
- work.makePortable();
+ var promise = new Promise();
+ promise.future = this;
+
+ FutureWork.run(work, promise);
}
+ else
#end
-
- FutureWork.run(dispatchWorkFunction, work, promise, useThreads ? MULTI_THREADED : SINGLE_THREADED, true);
+ {
+ try
+ {
+ value = work();
+ isComplete = true;
+ }
+ catch (e:Dynamic)
+ {
+ error = e;
+ isError = true;
+ }
+ }
}
}
@@ -189,6 +201,7 @@ import lime.utils.Log;
**/
public function ready(waitTime:Int = -1):Future
{
+ #if (lime_threads && !html5)
if (isComplete || isError)
{
return this;
@@ -196,34 +209,22 @@ import lime.utils.Log;
else
{
var time = System.getTimer();
- var prevTime = time;
var end = time + waitTime;
- while (!isComplete && !isError && time <= end)
+ while (!isComplete && !isError && time <= end && FutureWork.activeJobs > 0)
{
- if (FutureWork.activeJobs < 1)
- {
- Log.error('Cannot block for a Future without a "work" function.');
- return this;
- }
-
- if (FutureWork.singleThreadPool != null && FutureWork.singleThreadPool.activeJobs > 0)
- {
- @:privateAccess FutureWork.singleThreadPool.__update(time - prevTime);
- }
- else
- {
- #if sys
- Sys.sleep(0.01);
- #end
- }
+ #if sys
+ Sys.sleep(0.01);
+ #end
- prevTime = time;
time = System.getTimer();
}
return this;
}
+ #else
+ return this;
+ #end
}
/**
@@ -305,41 +306,9 @@ import lime.utils.Log;
future.value = value;
return future;
}
-
- /**
- Creates a `Future` instance which will asynchronously compute a value.
-
- Once `work()` returns a non-null value, the `Future` will finish with that value.
- If `work()` throws an error, the `Future` will finish with that error instead.
- @param work A function that computes a value of type `T`.
- @param state An argument to pass to `work()`. As this may be used on another thread, the
- main thread must not access or modify `state` until the `Future` finishes.
- @param mode Whether to use real threads as opposed to green threads. Green threads rely
- on cooperative multitasking, meaning `work()` must return periodically to allow other code
- enough time to run. In these cases, `work()` should return null to signal that it isn't finished.
- @return A new `Future` instance.
- @see https://en.wikipedia.org/wiki/Cooperative_multitasking
- **/
- public static function withEventualValue(work:WorkFunction Null>, state:State, mode:ThreadMode = #if html5 SINGLE_THREADED #else MULTI_THREADED #end):Future
- {
- var future = new Future();
- var promise = new Promise();
- promise.future = future;
-
- FutureWork.run(work, state, promise, mode);
-
- return future;
- }
-
- /**
- (For backwards compatibility.) Dispatches the given zero-argument function.
- **/
- @:noCompletion private static function dispatchWorkFunction(work:WorkFunction T>):Null
- {
- return work.dispatch();
- }
}
+#if (lime_threads && !html5)
/**
The class that handles asynchronous `work` functions passed to `new Future()`.
**/
@@ -349,158 +318,78 @@ import lime.utils.Log;
#end
@:dox(hide) class FutureWork
{
- @:allow(lime.app.Future)
- private static var singleThreadPool:ThreadPool;
- #if lime_threads
- private static var multiThreadPool:ThreadPool;
- // It isn't safe to pass a promise object to a web worker, but since it's
- // `@:generic` we can't store it as `Promise`. Instead, we'll store
- // the two methods we need.
- private static var promises:Map Dynamic, error:Dynamic -> Dynamic}> = new Map();
- #end
+ private static var threadPool:ThreadPool;
+ private static var promises:MapDynamic, error:Dynamic->Dynamic}>;
+
public static var minThreads(default, set):Int = 0;
public static var maxThreads(default, set):Int = 1;
public static var activeJobs(get, never):Int;
- private static function getPool(mode:ThreadMode):ThreadPool
- {
- #if lime_threads
- if (mode == MULTI_THREADED) {
- if(multiThreadPool == null) {
- multiThreadPool = new ThreadPool(minThreads, maxThreads, MULTI_THREADED);
- multiThreadPool.onComplete.add(multiThreadPool_onComplete);
- multiThreadPool.onError.add(multiThreadPool_onError);
- }
- return multiThreadPool;
- }
- #end
- if(singleThreadPool == null) {
- singleThreadPool = new ThreadPool(minThreads, maxThreads, SINGLE_THREADED);
- singleThreadPool.onComplete.add(singleThreadPool_onComplete);
- singleThreadPool.onError.add(singleThreadPool_onError);
- }
- return singleThreadPool;
- }
-
@:allow(lime.app.Future)
- private static function run(work:WorkFunctionNull>, state:State, promise:Promise, mode:ThreadMode = MULTI_THREADED, legacyCode:Bool = false):Void
+ private static function run(work:Void->T, promise:Promise):Void
{
- var bundle = {work: work, state: state, promise: promise, legacyCode: legacyCode};
-
- #if lime_threads
- if (mode == MULTI_THREADED)
+ if (threadPool == null)
{
- #if html5
- work.makePortable();
- #end
+ threadPool = new ThreadPool(minThreads, maxThreads, MULTI_THREADED);
+ threadPool.onComplete.add(threadPool_onComplete);
+ threadPool.onError.add(threadPool_onError);
- bundle.promise = null;
+ promises = new Map();
}
- #end
-
- var jobID:Int = getPool(mode).run(threadPool_doWork, bundle);
- #if lime_threads
- if (mode == MULTI_THREADED)
- {
- promises[jobID] = {complete: promise.complete, error: promise.error};
- }
- #end
+ var jobID:Int = threadPool.run(threadPool_doWork, work);
+ promises[jobID] = {complete: promise.complete, error: promise.error};
}
// Event Handlers
- private static function threadPool_doWork(bundle:{work:WorkFunctionDynamic>, state:State, legacyCode:Bool}, output:WorkOutput):Void
+ private static function threadPool_doWork(work:Void->Dynamic, output:WorkOutput):Void
{
try
{
- var result = bundle.work.dispatch(bundle.state);
- if (result != null || bundle.legacyCode)
- {
- #if (lime_threads && html5)
- bundle.work.makePortable();
- #end
- output.sendComplete(result);
- }
+ output.sendComplete(work());
}
catch (e:Dynamic)
{
- #if (lime_threads && html5)
- bundle.work.makePortable();
- #end
output.sendError(e);
}
}
- private static function singleThreadPool_onComplete(result:Dynamic):Void
+ private static function threadPool_onComplete(result:Dynamic):Void
{
- singleThreadPool.activeJob.state.promise.complete(result);
- }
-
- private static function singleThreadPool_onError(error:Dynamic):Void
- {
- singleThreadPool.activeJob.state.promise.error(error);
- }
-
- #if lime_threads
- private static function multiThreadPool_onComplete(result:Dynamic):Void
- {
- var promise = promises[multiThreadPool.activeJob.id];
- promises.remove(multiThreadPool.activeJob.id);
+ var promise = promises[threadPool.activeJob.id];
+ promises.remove(threadPool.activeJob.id);
promise.complete(result);
}
- private static function multiThreadPool_onError(error:Dynamic):Void
+ private static function threadPool_onError(error:Dynamic):Void
{
- var promise = promises[multiThreadPool.activeJob.id];
- promises.remove(multiThreadPool.activeJob.id);
+ var promise = promises[threadPool.activeJob.id];
+ promises.remove(threadPool.activeJob.id);
promise.error(error);
}
- #end
// Getters & Setters
@:noCompletion private static inline function set_minThreads(value:Int):Int
{
- if (singleThreadPool != null)
- {
- singleThreadPool.minThreads = value;
- }
- #if lime_threads
- if (multiThreadPool != null)
+ if (threadPool != null)
{
- multiThreadPool.minThreads = value;
+ threadPool.minThreads = value;
}
- #end
return minThreads = value;
}
@:noCompletion private static inline function set_maxThreads(value:Int):Int
{
- if (singleThreadPool != null)
+ if (threadPool != null)
{
- singleThreadPool.maxThreads = value;
+ threadPool.maxThreads = value;
}
- #if lime_threads
- if (multiThreadPool != null)
- {
- multiThreadPool.maxThreads = value;
- }
- #end
return maxThreads = value;
}
- @:noCompletion private static function get_activeJobs():Int
+ @:noCompletion private static inline function get_activeJobs():Int
{
- var sum:Int = 0;
- if (singleThreadPool != null)
- {
- sum += singleThreadPool.activeJobs;
- }
- #if lime_threads
- if (multiThreadPool != null)
- {
- sum += multiThreadPool.activeJobs;
- }
- #end
- return sum;
+ return threadPool != null ? threadPool.activeJobs : 0;
}
}
+#end
diff --git a/src/lime/graphics/Image.hx b/src/lime/graphics/Image.hx
index c825d9d84d..97385cdc3a 100644
--- a/src/lime/graphics/Image.hx
+++ b/src/lime/graphics/Image.hx
@@ -1002,7 +1002,7 @@ class Image
return promise.future;
#else
- return Future.withEventualValue(fromBytes, bytes, MULTI_THREADED);
+ return new Future(fromBytes.bind(bytes), true);
#end
}
diff --git a/src/lime/graphics/RenderContextAttributes.hx b/src/lime/graphics/RenderContextAttributes.hx
index 1e507da80d..059e0a4899 100644
--- a/src/lime/graphics/RenderContextAttributes.hx
+++ b/src/lime/graphics/RenderContextAttributes.hx
@@ -32,6 +32,13 @@ typedef RenderContextAttributes =
**/
@:optional var hardware:Bool;
+ #if html5
+ /**
+ Whether to preserve the `HTMLCanvas`'s image data after rendering
+ **/
+ @:optional var preserveDrawingBuffer:Bool;
+ #end
+
/**
Whether a stencil buffer is enabled
**/
diff --git a/src/lime/graphics/cairo/Cairo.hx b/src/lime/graphics/cairo/Cairo.hx
index 5dca32c8f9..353033fbf0 100644
--- a/src/lime/graphics/cairo/Cairo.hx
+++ b/src/lime/graphics/cairo/Cairo.hx
@@ -425,7 +425,7 @@ class Cairo
public function transform(matrix:Matrix3):Void
{
#if (lime_cffi && lime_cairo && !macro)
- NativeCFFI.lime_cairo_transform(handle, matrix);
+ NativeCFFI.lime_cairo_transform(handle, matrix.toCairoMatrix3());
#end
}
@@ -643,7 +643,7 @@ class Cairo
{
#if (lime_cffi && lime_cairo && !macro)
#if hl
- return NativeCFFI.lime_cairo_get_matrix(handle, new Matrix3());
+ return NativeCFFI.lime_cairo_get_matrix(handle, new CairoMatrix3());
#else
var m:Dynamic = NativeCFFI.lime_cairo_get_matrix(handle);
return new Matrix3(m.a, m.b, m.c, m.d, m.tx, m.ty);
@@ -657,7 +657,7 @@ class Cairo
{
#if (lime_cffi && lime_cairo && !macro)
#if hl
- NativeCFFI.lime_cairo_set_matrix(handle, value);
+ NativeCFFI.lime_cairo_set_matrix(handle, value.toCairoMatrix3());
#else
NativeCFFI.lime_cairo_set_matrix(handle, value.a, value.b, value.c, value.d, value.tx, value.ty);
// NativeCFFI.lime_cairo_set_matrix (handle, value);
diff --git a/src/lime/graphics/cairo/CairoPattern.hx b/src/lime/graphics/cairo/CairoPattern.hx
index 114f4c59d9..656410b6e5 100644
--- a/src/lime/graphics/cairo/CairoPattern.hx
+++ b/src/lime/graphics/cairo/CairoPattern.hx
@@ -127,7 +127,7 @@ abstract CairoPattern(CFFIPointer) from CFFIPointer to CFFIPointer
{
#if (lime_cffi && lime_cairo && !macro)
#if hl
- return NativeCFFI.lime_cairo_pattern_get_matrix(this, new Matrix3());
+ return NativeCFFI.lime_cairo_pattern_get_matrix(this, new CairoMatrix3());
#else
var m:Dynamic = NativeCFFI.lime_cairo_pattern_get_matrix(this);
return new Matrix3(m.a, m.b, m.c, m.d, m.tx, m.ty);
@@ -140,7 +140,7 @@ abstract CairoPattern(CFFIPointer) from CFFIPointer to CFFIPointer
@:noCompletion private function set_matrix(value:Matrix3):Matrix3
{
#if (lime_cffi && lime_cairo && !macro)
- NativeCFFI.lime_cairo_pattern_set_matrix(this, value);
+ NativeCFFI.lime_cairo_pattern_set_matrix(this, value.toCairoMatrix3());
#end
return value;
diff --git a/src/lime/math/Matrix3.hx b/src/lime/math/Matrix3.hx
index fdb460defa..330cad97c9 100644
--- a/src/lime/math/Matrix3.hx
+++ b/src/lime/math/Matrix3.hx
@@ -1,5 +1,7 @@
package lime.math;
+import lime.utils.Float32Array;
+
/**
`Matrix3` is a 3x3 transformation matrix particularly useful for
two-dimensional transformation. It can be used for rotation, scale
@@ -13,8 +15,9 @@ package lime.math;
[ b, d, ty ]
[ 0, 0, 1 ]
```
+
+ Values are stored in column-major order for GLSL compatibility.
**/
-import lime.utils.Float32Array;
#if hl
@:keep
#end
@@ -22,7 +25,7 @@ import lime.utils.Float32Array;
@:fileXml('tags="haxe,release"')
@:noDebug
#end
-abstract Matrix3(Float32Array) from Float32Array to Float32Array
+abstract Matrix3(Float32Array) to Float32Array
{
/**
The matrix a component, used in scaling and skewing (default is 1)
@@ -54,8 +57,6 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
**/
public var ty(get, set):Float;
- private static var __identity = new Matrix3();
-
/**
Creates a new `Matrix` instance
@param a (Optional) An initial a component value (default is 1)
@@ -67,10 +68,11 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
**/
public function new(a:Float = 1, b:Float = 0, c:Float = 0, d:Float = 1, tx:Float = 0, ty:Float = 0)
{
+ // Column-major order means adjacent values form a column, not a row.
this = new Float32Array([
- a, c, 0,
- b, d, 0,
- tx, ty, 1
+ a, b, 0, // column 0
+ c, d, 0, // column 1
+ tx, ty, 1 // column 2
]);
}
@@ -330,6 +332,21 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
return result;
}
+ @:from private static inline function fromCairoMatrix3(matrix:CairoMatrix3):Matrix3
+ {
+ return new Matrix3(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
+ }
+
+ @:from private static inline function fromFloat32Array(array:Float32Array):Matrix3
+ {
+ if (array.length != 9)
+ {
+ throw "Expected array of length 9, got " + array.length;
+ }
+
+ return cast array;
+ }
+
/**
Resets the matrix to default identity values
**/
@@ -374,12 +391,6 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
return this;
}
- // public inline function mult (m:Matrix3) {
- // var result = clone ();
- // result.concat (m);
- // return result;
- // }
-
/**
Applies rotation to the current matrix
@param theta A rotation value in degrees
@@ -389,9 +400,9 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
/*
Rotate object "after" other transforms
- [ a b 0 ][ ma mb 0 ]
- [ c d 0 ][ mc md 0 ]
- [ tx ty 1 ][ mtx mty 1 ]
+ [ a c tx ][ ma mc mtx ]
+ [ b d ty ][ mb md mty ]
+ [ 0 0 1 ][ 0 0 1 ]
ma = md = cos
mb = sin
@@ -427,9 +438,9 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
Scale object "after" other transforms
- [ a b 0 ][ sx 0 0 ]
- [ c d 0 ][ 0 sy 0 ]
- [ tx ty 1 ][ 0 0 1 ]
+ [ a c tx ][ sx 0 0 ]
+ [ b d ty ][ 0 sy 0 ]
+ [ 0 0 1 ][ 0 0 1 ]
*/
a *= sx;
@@ -467,43 +478,14 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
set_ty(ty);
}
- @:dox(hide) @:noCompletion public inline function to3DString(roundPixels:Bool = false):String
+ @:dox(hide) @:noCompletion @:to public inline function toCairoMatrix3():CairoMatrix3
{
- // identity matrix
- // [a,b,tx,0],
- // [c,d,ty,0],
- // [0,0,1, 0],
- // [0,0,0, 1]
- //
- // matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1)
-
- if (roundPixels)
- {
- return "matrix3d("
- + a
- + ", "
- + b
- + ", "
- + "0, 0, "
- + c
- + ", "
- + d
- + ", "
- + "0, 0, 0, 0, 1, 0, "
- + Std.int(tx)
- + ", "
- + Std.int(ty)
- + ", 0, 1)";
- }
- else
- {
- return "matrix3d(" + a + ", " + b + ", " + "0, 0, " + c + ", " + d + ", " + "0, 0, 0, 0, 1, 0, " + tx + ", " + ty + ", 0, 1)";
- }
+ return new CairoMatrix3(a, b, c, d, tx, ty);
}
@:dox(hide) public inline function toString():String
{
- return "matrix(" + a + ", " + b + ", " + c + ", " + d + ", " + tx + ", " + ty + ")";
+ return 'matrix($a, $b, $c, $d, $tx, $ty)';
}
/**
@@ -586,20 +568,20 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
inline function get_b():Float
{
- return this[3];
+ return this[1];
}
inline function set_b(value: Float):Float
{
- return this[3] = value;
+ return this[1] = value;
}
inline function get_c():Float
{
- return this[1];
+ return this[3];
}
inline function set_c(value: Float):Float
{
- return this[1] = value;
+ return this[3] = value;
}
inline function get_d():Float
@@ -640,3 +622,26 @@ abstract Matrix3(Float32Array) from Float32Array to Float32Array
return value;
}
}
+
+/**
+ An object with the same data as a `Matrix3`, in Cairo's expected format.
+**/
+class CairoMatrix3
+{
+ public var a:Float;
+ public var b:Float;
+ public var c:Float;
+ public var d:Float;
+ public var tx:Float;
+ public var ty:Float;
+
+ public function new(a:Float = 1, b:Float = 0, c:Float = 0, d:Float = 1, tx:Float = 0, ty:Float = 0)
+ {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.tx = tx;
+ this.ty = ty;
+ }
+}
diff --git a/src/lime/media/AudioBuffer.hx b/src/lime/media/AudioBuffer.hx
index ff067229ab..e74b531ec3 100644
--- a/src/lime/media/AudioBuffer.hx
+++ b/src/lime/media/AudioBuffer.hx
@@ -31,12 +31,41 @@ import flash.net.URLRequest;
@:fileXml('tags="haxe,release"')
@:noDebug
#end
+
+/**
+ The `AudioBuffer` class represents a buffer of audio data that can be played back using an `AudioSource`.
+ It supports a variety of audio formats and platforms, providing a consistent API for loading and managing audio data.
+
+ Depending on the platform, the audio backend may differ, but the class provides a unified interface for accessing
+ audio data, whether it's stored in memory, loaded from a file, or streamed.
+
+ @see lime.media.AudioSource
+**/
class AudioBuffer
{
+ /**
+ The number of bits per sample in the audio data.
+ **/
public var bitsPerSample:Int;
+
+ /**
+ The number of audio channels (e.g., 1 for mono, 2 for stereo).
+ **/
public var channels:Int;
+
+ /**
+ The raw audio data stored as a `UInt8Array`.
+ **/
public var data:UInt8Array;
+
+ /**
+ The sample rate of the audio data, in Hz.
+ **/
public var sampleRate:Int;
+
+ /**
+ The source of the audio data. This can be an `Audio`, `Sound`, `Howl`, or other platform-specific object.
+ **/
public var src(get, set):Dynamic;
@:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end;
@@ -57,8 +86,14 @@ class AudioBuffer
}
#end
+ /**
+ Creates a new, empty `AudioBuffer` instance.
+ **/
public function new() {}
+ /**
+ Disposes of the resources used by this `AudioBuffer`, such as unloading any associated audio data.
+ **/
public function dispose():Void
{
#if (js && html5 && lime_howlerjs)
@@ -66,6 +101,12 @@ class AudioBuffer
#end
}
+ /**
+ Creates an `AudioBuffer` from a Base64-encoded string.
+
+ @param base64String The Base64-encoded audio data.
+ @return An `AudioBuffer` instance with the decoded audio data.
+ **/
public static function fromBase64(base64String:String):AudioBuffer
{
if (base64String == null) return null;
@@ -112,6 +153,12 @@ class AudioBuffer
return null;
}
+ /**
+ Creates an `AudioBuffer` from a `Bytes` object.
+
+ @param bytes The `Bytes` object containing the audio data.
+ @return An `AudioBuffer` instance with the decoded audio data.
+ **/
public static function fromBytes(bytes:Bytes):AudioBuffer
{
if (bytes == null) return null;
@@ -145,6 +192,12 @@ class AudioBuffer
return null;
}
+ /**
+ Creates an `AudioBuffer` from a file.
+
+ @param path The file path to the audio data.
+ @return An `AudioBuffer` instance with the audio data loaded from the file.
+ **/
public static function fromFile(path:String):AudioBuffer
{
if (path == null) return null;
@@ -196,6 +249,12 @@ class AudioBuffer
#end
}
+ /**
+ Creates an `AudioBuffer` from an array of file paths.
+
+ @param paths An array of file paths to search for audio data.
+ @return An `AudioBuffer` instance with the audio data loaded from the first valid file found.
+ **/
public static function fromFiles(paths:Array):AudioBuffer
{
#if (js && html5 && lime_howlerjs)
@@ -221,7 +280,14 @@ class AudioBuffer
#end
}
+ /**
+ Creates an `AudioBuffer` from a `VorbisFile`.
+
+ @param vorbisFile The `VorbisFile` object containing the audio data.
+ @return An `AudioBuffer` instance with the decoded audio data.
+ **/
#if lime_vorbis
+
public static function fromVorbisFile(vorbisFile:VorbisFile):AudioBuffer
{
if (vorbisFile == null) return null;
@@ -243,6 +309,12 @@ class AudioBuffer
}
#end
+ /**
+ Asynchronously loads an `AudioBuffer` from a file.
+
+ @param path The file path to the audio data.
+ @return A `Future` that resolves to the loaded `AudioBuffer`.
+ **/
public static function loadFromFile(path:String):Future
{
#if (flash || (js && html5))
@@ -307,6 +379,12 @@ class AudioBuffer
#end
}
+ /**
+ Asynchronously loads an `AudioBuffer` from multiple files.
+
+ @param paths An array of file paths to search for audio data.
+ @return A `Future` that resolves to the loaded `AudioBuffer`.
+ **/
public static function loadFromFiles(paths:Array):Future
{
#if (js && html5 && lime_howlerjs)
@@ -335,7 +413,7 @@ class AudioBuffer
return promise.future;
#else
- return Future.withEventualValue(fromFiles, paths, MULTI_THREADED);
+ return new Future(fromFiles.bind(paths), true);
#end
}
diff --git a/src/lime/media/AudioSource.hx b/src/lime/media/AudioSource.hx
index ab0992e4fe..2ccc9b1465 100644
--- a/src/lime/media/AudioSource.hx
+++ b/src/lime/media/AudioSource.hx
@@ -9,20 +9,71 @@ import lime.math.Vector4;
@:fileXml('tags="haxe,release"')
@:noDebug
#end
+/**
+ The `AudioSource` class provides a way to control audio playback in a Lime application.
+ It allows for playing, pausing, and stopping audio, as well as controlling various
+ audio properties such as gain, pitch, and looping.
+
+ Depending on the platform, the audio backend may vary, but the API remains consistent.
+
+ @see lime.media.AudioBuffer
+**/
class AudioSource
{
+ /**
+ An event that is dispatched when the audio playback is complete.
+ **/
public var onComplete = new EventVoid>();
+
+ /**
+ The `AudioBuffer` associated with this `AudioSource`.
+ **/
public var buffer:AudioBuffer;
+
+ /**
+ The current playback position of the audio, in milliseconds.
+ **/
public var currentTime(get, set):Int;
+
+ /**
+ The gain (volume) of the audio. A value of `1.0` represents the default volume.
+ **/
public var gain(get, set):Float;
+
+ /**
+ The length of the audio, in milliseconds.
+ **/
public var length(get, set):Int;
+
+ /**
+ The number of times the audio will loop. A value of `0` means the audio will not loop.
+ **/
public var loops(get, set):Int;
+
+ /**
+ The pitch of the audio. A value of `1.0` represents the default pitch.
+ **/
public var pitch(get, set):Float;
+
+ /**
+ The offset within the audio buffer to start playback, in samples.
+ **/
public var offset:Int;
+
+ /**
+ The 3D position of the audio source, represented as a `Vector4`.
+ **/
public var position(get, set):Vector4;
@:noCompletion private var __backend:AudioSourceBackend;
+ /**
+ Creates a new `AudioSource` instance.
+ @param buffer The `AudioBuffer` to associate with this `AudioSource`.
+ @param offset The starting offset within the audio buffer, in samples.
+ @param length The length of the audio to play, in milliseconds. If `null`, the full buffer is used.
+ @param loops The number of times to loop the audio. `0` means no looping.
+ **/
public function new(buffer:AudioBuffer = null, offset:Int = 0, length:Null = null, loops:Int = 0)
{
this.buffer = buffer;
@@ -43,6 +94,9 @@ class AudioSource
}
}
+ /**
+ Releases any resources used by this `AudioSource`.
+ **/
public function dispose():Void
{
__backend.dispose();
@@ -53,16 +107,25 @@ class AudioSource
__backend.init();
}
+ /**
+ Starts or resumes audio playback.
+ **/
public function play():Void
{
__backend.play();
}
+ /**
+ Pauses audio playback.
+ **/
public function pause():Void
{
__backend.pause();
}
+ /**
+ Stops audio playback and resets the playback position to the beginning.
+ **/
public function stop():Void
{
__backend.stop();
diff --git a/src/lime/media/openal/ALC.hx b/src/lime/media/openal/ALC.hx
index 3cc1fa8517..56a6350082 100644
--- a/src/lime/media/openal/ALC.hx
+++ b/src/lime/media/openal/ALC.hx
@@ -84,12 +84,13 @@ class ALC
public static function getContextsDevice(context:ALContext):ALDevice
{
- #if (lime_cffi && lime_openal && !macro) #if !hl var handle:Dynamic = NativeCFFI.lime_alc_get_contexts_device(context);
+ #if (lime_cffi && lime_openal && !macro)
+ var handle:Dynamic = NativeCFFI.lime_alc_get_contexts_device(context);
if (handle != null)
{
return new ALDevice(handle);
- } #else #end
+ }
#end
return null;
diff --git a/src/lime/system/BackgroundWorker.hx b/src/lime/system/BackgroundWorker.hx
index 5be17e9eae..f92ca0ca17 100644
--- a/src/lime/system/BackgroundWorker.hx
+++ b/src/lime/system/BackgroundWorker.hx
@@ -1,4 +1,231 @@
package lime.system;
-@:deprecated("Replace references to lime.system.BackgroundWorker with lime.system.ThreadPool. As the API is identical, no other changes are necessary.")
-typedef BackgroundWorker = ThreadPool;
+import lime.app.Application;
+import lime.app.Event;
+#if sys
+#if haxe4
+import sys.thread.Deque;
+import sys.thread.Thread;
+#elseif cpp
+import cpp.vm.Deque;
+import cpp.vm.Thread;
+#elseif neko
+import neko.vm.Deque;
+import neko.vm.Thread;
+#end
+#end
+
+/**
+ A `BackgroundWorker` allows the execution of a function on a background thread,
+ avoiding the blocking of the main thread. This is particularly useful for long-running
+ operations like file I/O, network requests, or computationally intensive tasks.
+
+ ### Notes:
+ - **Thread Support:** Only system targets (such as C++, Neko) support threading.
+ - **Events:** The class uses the `Event` class to dispatch completion, error,
+ and progress notifications.
+
+ @see `ThreadPool` for more advanced threading capabilities, including thread
+ safety, HTML5 threads, and more robust handling of tasks.
+**/
+#if !lime_debug
+@:fileXml('tags="haxe,release"')
+@:noDebug
+#end
+class BackgroundWorker
+{
+ private static var MESSAGE_COMPLETE = "__COMPLETE__";
+ private static var MESSAGE_ERROR = "__ERROR__";
+
+ /**
+ Indicates whether the worker has been canceled.
+ **/
+ public var canceled(default, null):Bool;
+
+ /**
+ Indicates whether the worker has completed its task.
+ **/
+ public var completed(default, null):Bool;
+
+ /**
+ Dispatched when the worker is about to perform its task.
+ The function to execute should be added as a listener to this event.
+ **/
+ public var doWork = new EventVoid>();
+
+ /**
+ Dispatched when the worker has successfully completed its task.
+ **/
+ public var onComplete = new EventVoid>();
+
+ /**
+ Dispatched if an error occurs during the execution of the worker's task.
+ **/
+ public var onError = new EventVoid>();
+
+ /**
+ Dispatched periodically during the worker's task to provide progress updates.
+ **/
+ public var onProgress = new EventVoid>();
+
+ @:noCompletion private var __runMessage:Dynamic;
+ #if (cpp || neko)
+ @:noCompletion private var __messageQueue:Deque;
+ @:noCompletion private var __workerThread:Thread;
+ #end
+
+ /**
+ Creates a new `BackgroundWorker` instance.
+ **/
+ public function new() {}
+
+ /**
+ Cancels the worker's task if it is still running. This won't stop the thread
+ immediately.
+ **/
+ public function cancel():Void
+ {
+ canceled = true;
+
+ #if (cpp || neko)
+ __workerThread = null;
+ #end
+ }
+
+ /**
+ Starts the worker's task, optionally passing a message to the task.
+ @param message An optional message to pass to the worker's task.
+ **/
+ public function run(message:Dynamic = null):Void
+ {
+ canceled = false;
+ completed = false;
+ __runMessage = message;
+
+ #if (cpp || neko)
+ __messageQueue = new Deque();
+ __workerThread = Thread.create(__doWork);
+
+ // TODO: Better way to do this
+
+ if (Application.current != null)
+ {
+ Application.current.onUpdate.add(__update);
+ }
+ #else
+ __doWork();
+ #end
+ }
+
+ /**
+ Sends a completion message, indicating that the worker has finished its task.
+ @param message An optional message to pass to the `onComplete` event.
+ **/
+ public function sendComplete(message:Dynamic = null):Void
+ {
+ completed = true;
+
+ #if (cpp || neko)
+ __messageQueue.add(MESSAGE_COMPLETE);
+ __messageQueue.add(message);
+ #else
+ if (!canceled)
+ {
+ canceled = true;
+ onComplete.dispatch(message);
+ }
+ #end
+ }
+
+ /**
+ Sends an error message, indicating that an error occurred during the worker's task.
+ @param message An optional message to pass to the `onError` event.
+ **/
+ public function sendError(message:Dynamic = null):Void
+ {
+ #if (cpp || neko)
+ __messageQueue.add(MESSAGE_ERROR);
+ __messageQueue.add(message);
+ #else
+ if (!canceled)
+ {
+ canceled = true;
+ onError.dispatch(message);
+ }
+ #end
+ }
+
+ /**
+ Sends a progress update message.
+ @param message An optional message to pass to the `onProgress` event.
+ **/
+ public function sendProgress(message:Dynamic = null):Void
+ {
+ #if (cpp || neko)
+ __messageQueue.add(message);
+ #else
+ if (!canceled)
+ {
+ onProgress.dispatch(message);
+ }
+ #end
+ }
+
+ @:noCompletion private function __doWork():Void
+ {
+ doWork.dispatch(__runMessage);
+
+ // #if (cpp || neko)
+ //
+ // __messageQueue.add (MESSAGE_COMPLETE);
+ //
+ // #else
+ //
+ // if (!canceled) {
+ //
+ // canceled = true;
+ // onComplete.dispatch (null);
+ //
+ // }
+ //
+ // #end
+ }
+
+ @:noCompletion private function __update(deltaTime:Int):Void
+ {
+ #if (cpp || neko)
+ var message = __messageQueue.pop(false);
+
+ if (message != null)
+ {
+ if (message == MESSAGE_ERROR)
+ {
+ Application.current.onUpdate.remove(__update);
+
+ if (!canceled)
+ {
+ canceled = true;
+ onError.dispatch(__messageQueue.pop(false));
+ }
+ }
+ else if (message == MESSAGE_COMPLETE)
+ {
+ Application.current.onUpdate.remove(__update);
+
+ if (!canceled)
+ {
+ canceled = true;
+ onComplete.dispatch(__messageQueue.pop(false));
+ }
+ }
+ else
+ {
+ if (!canceled)
+ {
+ onProgress.dispatch(message);
+ }
+ }
+ }
+ #end
+ }
+}
diff --git a/src/lime/system/CFFI.hx b/src/lime/system/CFFI.hx
index 568ef394fa..3f512807ec 100644
--- a/src/lime/system/CFFI.hx
+++ b/src/lime/system/CFFI.hx
@@ -165,6 +165,11 @@ class CFFI
{
result = __tryLoad(ndllFolder + __sysName() + "64/" + library, library, method, args);
}
+
+ if (result == null)
+ {
+ result = __tryLoad(ndllFolder + __sysName() + "Arm64/" + library, library, method, args);
+ }
}
}
diff --git a/src/lime/system/Clipboard.hx b/src/lime/system/Clipboard.hx
index be4516795a..f0cb891336 100644
--- a/src/lime/system/Clipboard.hx
+++ b/src/lime/system/Clipboard.hx
@@ -46,6 +46,8 @@ class Clipboard
{
_text = FlashClipboard.generalClipboard.getData(TEXT_FORMAT);
}
+ #elseif (js || html5)
+ _text = cacheText;
#end
__updated = true;
diff --git a/src/lime/system/ThreadPool.hx b/src/lime/system/ThreadPool.hx
index 1999920ebb..ec8669b295 100644
--- a/src/lime/system/ThreadPool.hx
+++ b/src/lime/system/ThreadPool.hx
@@ -15,12 +15,18 @@ import lime._internal.backend.html5.HTML5Thread as Thread;
#end
/**
- A simple and thread-safe way to run a one or more asynchronous jobs. It
- manages a queue of jobs, starting new ones once the old ones are done.
+ A thread pool executes one or more functions asynchronously.
- It can also keep a certain number of threads (configurable via `minThreads`)
- running in the background even when no jobs are available. This avoids the
- not-insignificant overhead of stopping and restarting threads.
+ In multi-threaded mode, jobs run on background threads. In HTML5, this means
+ using web workers, which impose additional restrictions (see below). In
+ single-threaded mode, jobs run between frames on the main thread. To avoid
+ blocking, these jobs should only do a small amount of work at a time.
+
+ In multi-threaded mode, the pool spins up new threads as jobs arrive (up to
+ `maxThreads`). If too many jobs arrive at once, it places them in a queue to
+ run when threads open up. If you run jobs frequently but not constantly, you
+ can also set `minThreads` to keep a certain number of threads alive,
+ avoiding the overhead of repeatedly spinning them up.
Sample usage:
@@ -33,13 +39,20 @@ import lime._internal.backend.html5.HTML5Thread as Thread;
threadPool.run(processFile, url);
}
- For thread safety, the worker function should only give output through the
- `WorkOutput` object it receives. Calling `output.sendComplete()` will
- trigger an `onComplete` event on the main thread.
-
- @see `lime.system.WorkOutput.WorkFunction` for important information about
- `doWork`.
- @see https://player03.com/openfl/threads-guide/ for a tutorial.
+ Guidelines to make your code work on all targets and configurations:
+
+ - For thread safety and web worker compatibility, your work function should
+ only return data through the `WorkOutput` object it receives.
+ - For web worker compatibility, you should only send data to your work
+ function via the `State` object. But since this can be any object, you can
+ put an arbitrary amount of data there.
+ - For web worker compatibility, your work function must be static, and you
+ can't `bind()` any extra arguments.
+ - For single-threaded performance, your function should only do a small
+ amount of work at a time. Store progress in the `State` object so you can
+ pick up where you left off. You don't have to worry about timing: just aim
+ to take a small fraction of the frame's time, and `ThreadPool` will keep
+ running the function until enough time passes.
**/
#if !lime_debug
@:fileXml('tags="haxe,release"')
@@ -63,15 +76,35 @@ class ThreadPool extends WorkOutput
#end
/**
- Indicates that no further events will be dispatched.
+ A rough estimate of how much of the app's time should be spent on
+ single-threaded `ThreadPool`s. For instance, the default value of 1/2
+ means they'll use about half the app's available time every frame.
+
+ The accuracy of this estimate depends on how often your work functions
+ return. If you find that a `ThreadPool` is taking longer than scheduled,
+ try making the work function return more often.
+ **/
+ public static var workLoad:Float = 1 / 2;
+
+ /**
+ __Access this only from the main thread.__
+
+ The sum of all active single-threaded pools' `workPriority` values.
**/
- public var canceled(default, null):Bool = false;
+ @:allow(lime.system.JobList)
+ private static var __totalWorkPriority:Float = 0;
/**
- Indicates that the latest job finished successfully, and no other job
- has been started/is ongoing.
+ Returns whether the caller called this function from the main thread.
**/
- public var completed(default, null):Bool = false;
+ public static inline function isMainThread():Bool
+ {
+ #if (haxe4 && lime_threads)
+ return Thread.current() == __mainThread;
+ #else
+ return true;
+ #end
+ }
/**
The number of live threads in this pool, including both active and idle
@@ -97,13 +130,6 @@ class ThreadPool extends WorkOutput
The maximum number of live threads this pool can have at once. If this
value decreases, active jobs will still be allowed to finish.
-
- You can set this in single-threaded mode, but it's rarely useful. For
- instance, suppose you have six jobs, each of which takes about a second.
- If you leave `maxThreads` at 1, then one will finish every second for
- six seconds. If you set `maxThreads = 6`, then none will finish for five
- seconds, and then they'll all finish at once. The total duration is
- unchanged, but none of them finish early.
**/
public var maxThreads:Int;
@@ -111,10 +137,8 @@ class ThreadPool extends WorkOutput
__Set this only from the main thread.__
The number of threads that will be kept alive at all times, even if
- there's no work to do. Setting this won't add new threads, it'll just
- keep existing ones running.
-
- Has no effect in single-threaded mode.
+ there's no work to do. Setting this won't immediately spin up new
+ threads; you must still call `run()` to get them started.
**/
public var minThreads:Int;
@@ -123,45 +147,57 @@ class ThreadPool extends WorkOutput
Dispatched at most once per job.
**/
public var onComplete(default, null) = new EventVoid>();
+
/**
Dispatched on the main thread when `doWork` calls `sendError()`.
Dispatched at most once per job.
**/
public var onError(default, null) = new EventVoid>();
+
/**
Dispatched on the main thread when `doWork` calls `sendProgress()`. May
be dispatched any number of times per job.
**/
public var onProgress(default, null) = new EventVoid>();
+
/**
Dispatched on the main thread when a new job begins. Dispatched exactly
once per job.
**/
public var onRun(default, null) = new EventVoid>();
+ /**
+ (Single-threaded mode only.) How important this pool's jobs are relative
+ to other single-threaded pools.
+
+ For instance, if all pools use the default priority of 1, they will all
+ run for an approximately equal amount of time each frame. If one has a
+ value of 2, it will run approximately twice as long as the others.
+ **/
+ public var workPriority(default, set):Float = 1;
+
@:deprecated("Instead pass the callback to ThreadPool.run().")
@:noCompletion @:dox(hide) public var doWork(get, never):PseudoEvent;
+
private var __doWork:WorkFunctionWorkOutput->Void>;
- private var __activeJobs:JobList = new JobList();
+ private var __activeJobs:JobList;
#if lime_threads
/**
The set of threads actively running a job.
**/
- private var __activeThreads:Map = new Map();
+ private var __activeThreads:Map;
/**
A list of idle threads. Not to be confused with `idleThreads`, a public
variable equal to `__idleThreads.length`.
**/
- private var __idleThreads:List = new List();
+ private var __idleThreads:Array;
#end
private var __jobQueue:JobList = new JobList();
- private var __workPerFrame:Float;
-
/**
__Call this only from the main thread.__
@@ -173,27 +209,23 @@ class ThreadPool extends WorkOutput
@param mode Defaults to `MULTI_THREADED` on most targets, but
`SINGLE_THREADED` in HTML5. In HTML5, `MULTI_THREADED` mode uses web
workers, which impose additional restrictions.
- @param workLoad (Single-threaded mode only) A rough estimate of how much
- of the app's time should be spent on this `ThreadPool`. For instance,
- the default value of 1/2 means this worker will take up about half the
- app's available time every frame. See `workIterations` for instructions
- to improve the accuracy of this estimate.
**/
- public function new(minThreads:Int = 0, maxThreads:Int = 1, mode:ThreadMode = null, workLoad:Float = 1/2)
+ public function new(minThreads:Int = 0, maxThreads:Int = 1, mode:ThreadMode = null)
{
super(mode);
- if (Application.current != null && Application.current.window != null)
- {
- __workPerFrame = workLoad / Application.current.window.frameRate;
- }
- else
- {
- __workPerFrame = workLoad / 60;
- }
+ __activeJobs = new JobList(this);
this.minThreads = minThreads;
this.maxThreads = maxThreads;
+
+ #if lime_threads
+ if (this.mode == MULTI_THREADED)
+ {
+ __activeThreads = new Map();
+ __idleThreads = [];
+ }
+ #end
}
/**
@@ -204,12 +236,10 @@ class ThreadPool extends WorkOutput
**/
public function cancel(error:Dynamic = null):Void
{
- #if (haxe4 && lime_threads)
- if (Thread.current() != __mainThread)
+ if (!isMainThread())
{
throw "Call cancel() only from the main thread.";
}
- #end
Application.current.onUpdate.remove(__update);
@@ -222,12 +252,12 @@ class ThreadPool extends WorkOutput
var thread:Thread = __activeThreads[job.id];
if (idleThreads < minThreads)
{
- thread.sendMessage(new ThreadEvent(WORK, null, null));
+ thread.sendMessage({event: CANCEL});
__idleThreads.push(thread);
}
else
{
- thread.sendMessage(new ThreadEvent(EXIT, null, null));
+ thread.sendMessage({event: EXIT});
}
}
#end
@@ -247,10 +277,10 @@ class ThreadPool extends WorkOutput
__activeJobs.clear();
#if lime_threads
- // Cancel idle threads if there are more than the minimum.
+ // Exit idle threads if there are more than the minimum.
while (idleThreads > minThreads)
{
- __idleThreads.pop().sendMessage(new ThreadEvent(EXIT, null, null));
+ __idleThreads.pop().sendMessage({event: EXIT});
}
#end
@@ -267,36 +297,25 @@ class ThreadPool extends WorkOutput
__jobComplete.value = false;
activeJob = null;
- completed = false;
- canceled = true;
}
/**
Cancels one active or queued job. Does not dispatch an error event.
- @param job A `JobData` object, or a job's unique `id`, `state`, or
- `doWork` function.
@return Whether a job was canceled.
**/
- public function cancelJob(job:JobIdentifier):Bool
+ public function cancelJob(jobID:Int):Bool
{
- var data:JobData = __activeJobs.get(job);
-
- if (data != null)
+ #if lime_threads
+ var thread:Thread = __activeThreads[jobID];
+ if (thread != null)
{
- #if lime_threads
- var thread:Thread = __activeThreads[data.id];
- if (thread != null)
- {
- thread.sendMessage(new ThreadEvent(WORK, null, null));
- __activeThreads.remove(data.id);
- __idleThreads.push(thread);
- }
- #end
-
- return __activeJobs.remove(data);
+ thread.sendMessage({event: CANCEL});
+ __activeThreads.remove(jobID);
+ __idleThreads.push(thread);
}
+ #end
- return __jobQueue.remove(__jobQueue.get(job));
+ return __activeJobs.remove(__activeJobs.get(jobID)) || __jobQueue.remove(__jobQueue.get(jobID));
}
/**
@@ -308,17 +327,21 @@ class ThreadPool extends WorkOutput
}
/**
- Queues a new job, to be run once a thread becomes available.
+ Runs the given function asynchronously, or queues it for later if all
+ threads are busy.
+ @param doWork The function to run. For best results, see the guidelines
+ in the `ThreadPool` class overview. In brief: `doWork` should be static,
+ only access its arguments, and return often.
+ @param state An object to pass to `doWork`, ideally a mutable object so
+ that `doWork` can save its progress.
@return The job's unique ID.
**/
public function run(doWork:WorkFunctionWorkOutput->Void> = null, state:State = null):Int
{
- #if (haxe4 && lime_threads)
- if (Thread.current() != __mainThread)
+ if (!isMainThread())
{
throw "Call run() only from the main thread.";
}
- #end
if (doWork == null)
{
@@ -338,11 +361,9 @@ class ThreadPool extends WorkOutput
}
var job:JobData = new JobData(doWork, state);
- __jobQueue.add(job);
- completed = false;
- canceled = false;
+ __jobQueue.push(job);
- if (Application.current != null && !Application.current.onUpdate.has(__update))
+ if (!Application.current.onUpdate.has(__update))
{
Application.current.onUpdate.add(__update);
}
@@ -361,6 +382,7 @@ class ThreadPool extends WorkOutput
**/
private static function __executeThread():Void
{
+ // @formatter:off
JSAsync.async({
var output:WorkOutput = #if html5 new WorkOutput(MULTI_THREADED) #else cast(Thread.readMessage(true), WorkOutput) #end;
var event:ThreadEvent = null;
@@ -374,7 +396,7 @@ class ThreadPool extends WorkOutput
{
event = Thread.readMessage(true);
}
- while (!#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (event, ThreadEvent));
+ while (event == null || !Reflect.hasField(event, "event"));
output.resetJobProgress();
}
@@ -417,9 +439,9 @@ class ThreadPool extends WorkOutput
if (interruption == null || output.__jobComplete.value)
{
// Work is done; wait for more.
- event = null;
+ event = interruption;
}
- else if(#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (interruption, ThreadEvent))
+ else if (Reflect.hasField(interruption, "event"))
{
// Work on the new job.
event = interruption;
@@ -433,6 +455,7 @@ class ThreadPool extends WorkOutput
// Do it all again.
}
});
+ // @formatter:on
}
#end
@@ -451,15 +474,13 @@ class ThreadPool extends WorkOutput
**/
private function __update(deltaTime:Int):Void
{
- #if (haxe4 && lime_threads)
- if (Thread.current() != __mainThread)
+ if (!isMainThread())
{
return;
}
- #end
// Process the queue.
- while (!__jobQueue.isEmpty() && activeJobs < maxThreads)
+ while (__jobQueue.length > 0 && activeJobs < maxThreads)
{
var job:JobData = __jobQueue.pop();
@@ -473,22 +494,27 @@ class ThreadPool extends WorkOutput
job.doWork.makePortable();
#end
- var thread:Thread = __idleThreads.isEmpty() ? createThread(__executeThread) : __idleThreads.pop();
+ var thread:Thread = __idleThreads.length == 0 ? createThread(__executeThread) : __idleThreads.pop();
__activeThreads[job.id] = thread;
- thread.sendMessage(new ThreadEvent(WORK, null, job));
+ thread.sendMessage({event: WORK, job: job});
}
#end
}
- // Run the next single-threaded job.
- if (mode == SINGLE_THREADED && activeJobs > 0)
+ // Run the next single-threaded job, if any.
+ if (mode == SINGLE_THREADED && __activeJobs.hasNext())
{
- activeJob = __activeJobs.pop();
+ activeJob = __activeJobs.next();
var state:State = activeJob.state;
__jobComplete.value = false;
workIterations.value = 0;
+ // `workLoad / frameRate` is the total time that pools may use per
+ // frame. `workPriority / __totalWorkPriority` is this pool's
+ // fraction of that total.
+ var maxTimeElapsed:Float = workPriority * workLoad / (__totalWorkPriority * Application.current.window.frameRate);
+
var startTime:Float = timestamp();
var timeElapsed:Float = 0;
try
@@ -499,7 +525,7 @@ class ThreadPool extends WorkOutput
activeJob.doWork.dispatch(state, this);
timeElapsed = timestamp() - startTime;
}
- while (!__jobComplete.value && timeElapsed < __workPerFrame);
+ while (!__jobComplete.value && timeElapsed < maxTimeElapsed);
}
catch (e:#if (haxe_ver >= 4.1) haxe.Exception #else Dynamic #end)
{
@@ -508,24 +534,25 @@ class ThreadPool extends WorkOutput
activeJob.duration += timeElapsed;
- // Add this job to the end of the list, to cycle through.
- __activeJobs.add(activeJob);
-
activeJob = null;
}
var threadEvent:ThreadEvent;
while ((threadEvent = __jobOutput.pop(false)) != null)
{
- if (!__activeJobs.exists(threadEvent.job))
+ if (threadEvent.jobID != null)
{
- // Ignore events from canceled jobs.
- continue;
+ activeJob = __activeJobs.get(threadEvent.jobID);
+ }
+ else
+ {
+ activeJob = threadEvent.job;
}
- // Get by ID because in HTML5, the object will have been cloned,
- // which will interfere with attempts to test equality.
- activeJob = __activeJobs.getByID(threadEvent.job.id);
+ if (activeJob == null || !__activeJobs.exists(activeJob))
+ {
+ continue;
+ }
if (mode == MULTI_THREADED)
{
@@ -558,9 +585,9 @@ class ThreadPool extends WorkOutput
var thread:Thread = __activeThreads[activeJob.id];
__activeThreads.remove(activeJob.id);
- if (currentThreads > maxThreads || __jobQueue.isEmpty() && currentThreads > minThreads)
+ if (currentThreads > maxThreads || __jobQueue.length == 0 && currentThreads > minThreads)
{
- thread.sendMessage(new ThreadEvent(EXIT, null, null));
+ thread.sendMessage({event: EXIT});
}
else
{
@@ -569,15 +596,13 @@ class ThreadPool extends WorkOutput
}
#end
- completed = threadEvent.event == COMPLETE && activeJobs == 0 && __jobQueue.isEmpty();
-
default:
}
activeJob = null;
}
- if (completed && Application.current != null)
+ if (activeJobs == 0 && __jobQueue.length == 0)
{
Application.current.onUpdate.remove(__update);
}
@@ -612,70 +637,172 @@ class ThreadPool extends WorkOutput
return activeJobs + idleThreads;
}
- // Note the distinction between `doWork` and `__doWork`: the former is for
- // backwards compatibility, while the latter is always used.
private function get_doWork():PseudoEvent
{
return this;
}
+
+ private function set_workPriority(value:Float):Float
+ {
+ if (mode == SINGLE_THREADED && activeJobs > 0)
+ {
+ __totalWorkPriority += value - workPriority;
+ }
+ return workPriority = value;
+ }
}
-@:access(lime.system.ThreadPool) @:forward(canceled)
-private abstract PseudoEvent(ThreadPool) from ThreadPool {
+@:access(lime.system.ThreadPool)
+private abstract PseudoEvent(ThreadPool) from ThreadPool
+{
@:noCompletion @:dox(hide) public var __listeners(get, never):Array;
- private inline function get___listeners():Array { return []; };
+
+ private inline function get___listeners():Array
+ {
+ return [];
+ };
+
@:noCompletion @:dox(hide) public var __repeat(get, never):Array;
- private inline function get___repeat():Array { return []; };
- public function add(callback:Dynamic -> Void):Void {
+ private inline function get___repeat():Array
+ {
+ return [];
+ };
+
+ public function add(callback:Dynamic->Void):Void
+ {
function callCallback(state:State, output:WorkOutput):Void
{
callback(state);
}
#if (lime_threads && html5)
- if (this.mode == MULTI_THREADED)
- throw "Unsupported operation; instead pass the callback to ThreadPool's constructor.";
+ if (this.mode == MULTI_THREADED) throw "Unsupported operation; instead pass the callback to ThreadPool's constructor.";
else
- this.__doWork = { func: callCallback };
+ this.__doWork = {func: callCallback};
#else
this.__doWork = callCallback;
#end
}
public inline function cancel():Void {}
+
public inline function dispatch():Void {}
- public inline function has(callback:Dynamic -> Void):Bool { return this.__doWork != null; }
- public inline function remove(callback:Dynamic -> Void):Void { this.__doWork = null; }
- public inline function removeAll():Void { this.__doWork = null; }
+
+ public inline function has(callback:Dynamic->Void):Bool
+ {
+ return this.__doWork != null;
+ }
+
+ public inline function remove(callback:Dynamic->Void):Void
+ {
+ this.__doWork = null;
+ }
+
+ public inline function removeAll():Void
+ {
+ this.__doWork = null;
+ }
}
-@:forward
-abstract JobList(List)
+class JobList
{
- public inline function new()
+ /**
+ * Whether `pool.workPriority` is being added to
+ * `ThreadPool.__totalWorkPriority`. Set this to true when `length > 0` and
+ * false when `length == 0`. The setter will ensure it is only added once.
+ */
+ @:allow(lime.system.ThreadPool)
+ private var __addingWorkPriority(default, set):Bool;
+
+ private var __index:Int = 0;
+
+ private var __jobs:Array = [];
+
+ public var length(get, never):Int;
+
+ public var pool(default, null):ThreadPool;
+
+ public inline function new(?pool:ThreadPool)
+ {
+ this.pool = pool;
+ @:bypassAccessor __addingWorkPriority = false;
+ }
+
+ public inline function clear():Void
{
- this = new List();
+ #if haxe4
+ __jobs.resize(0);
+ #else
+ __jobs = [];
+ #end
+ __addingWorkPriority = false;
}
public inline function exists(job:JobData):Bool
{
- return getByID(job.id) != null;
+ return get(job.id) != null;
}
- public inline function remove(job:JobData):Bool
+ public inline function hasNext():Bool
{
- return this.remove(job) || removeByID(job.id);
+ return __jobs.length > 0;
+ }
+
+ /**
+ Iterates in an endless loop, starting over upon reaching the end.
+ **/
+ public inline function next():JobData
+ {
+ __index++;
+ if (__index >= length)
+ {
+ __index = 0;
+ }
+
+ return __jobs[__index];
+ }
+
+ public inline function pop():JobData
+ {
+ var job:JobData = __jobs.pop();
+ __addingWorkPriority = length > 0;
+ return job;
+ }
+
+ public function remove(job:JobData):Bool
+ {
+ if (__jobs.remove(job))
+ {
+ __addingWorkPriority = length > 0;
+ return true;
+ }
+ else if (removeByID(job.id))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
public inline function removeByID(id:Int):Bool
{
- return this.remove(getByID(id));
+ if (__jobs.remove(get(id)))
+ {
+ __addingWorkPriority = length > 0;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
- public function getByID(id:Int):JobData
+ public function get(id:Int):JobData
{
- for (job in this)
+ for (job in __jobs)
{
if (job.id == id)
{
@@ -684,62 +811,36 @@ abstract JobList(List)
}
return null;
}
+ public inline function push(job:JobData):Void
+ {
+ __jobs.push(job);
+ __addingWorkPriority = true;
+ }
+
+ // Getters & Setters
- public function get(jobIdentifier:JobIdentifier):JobData
+ private inline function set___addingWorkPriority(value:Bool):Bool
{
- switch (jobIdentifier)
+ if (pool != null && __addingWorkPriority != value && ThreadPool.isMainThread())
{
- case ID(id):
- return getByID(id);
- case FUNCTION(doWork):
- for (job in this)
- {
- if (job.doWork == doWork)
- {
- return job;
- }
- }
- case STATE(state):
- for (job in this)
- {
- if (job.state == state)
- {
- return job;
- }
- }
+ if (value)
+ {
+ ThreadPool.__totalWorkPriority += pool.workPriority;
+ }
+ else
+ {
+ ThreadPool.__totalWorkPriority -= pool.workPriority;
+ }
+ return __addingWorkPriority = value;
+ }
+ else
+ {
+ return __addingWorkPriority;
}
- return null;
}
-}
-/**
- A piece of data that uniquely represents a job. This can be the integer ID
- (and integers will be assumed to be such), the `doWork` function, or the
- `JobData` object itself. Failing any of those, a value will be assumed to be
- the job's `state`.
-
- Caution: if the provided data isn't unique, such as a `doWork` function
- that's in use by multiple jobs, the wrong job may be selected or canceled.
-**/
-@:forward
-abstract JobIdentifier(JobIdentifierImpl) from JobIdentifierImpl {
- @:from private static inline function fromJob(job:JobData):JobIdentifier {
- return ID(job.id);
- }
- @:from private static inline function fromID(id:Int):JobIdentifier {
- return ID(id);
- }
- @:from private static inline function fromFunction(doWork:WorkFunctionWorkOutput->Void>):JobIdentifier {
- return FUNCTION(doWork);
- }
- @:from private static inline function fromState(state:State):JobIdentifier {
- return STATE(state);
+ private inline function get_length():Int
+ {
+ return __jobs.length;
}
}
-
-private enum JobIdentifierImpl
-{
- ID(id:Int);
- FUNCTION(doWork:WorkFunctionWorkOutput->Void>);
- STATE(state:State);
-}
diff --git a/src/lime/system/WorkOutput.hx b/src/lime/system/WorkOutput.hx
index 433fad10e3..c2673703bd 100644
--- a/src/lime/system/WorkOutput.hx
+++ b/src/lime/system/WorkOutput.hx
@@ -13,12 +13,10 @@ import neko.vm.Deque;
import neko.vm.Thread;
import neko.vm.Tls;
#end
-
#if html5
import lime._internal.backend.html5.HTML5Thread as Thread;
import lime._internal.backend.html5.HTML5Thread.Transferable;
#end
-
#if macro
import haxe.macro.Expr;
@@ -44,8 +42,9 @@ class WorkOutput
the current job, including (if applicable) the ongoing call.
In single-threaded mode, it only counts the number of calls this frame.
- This helps you adjust `doWork`'s length: too few iterations per frame
- means `workLoad` may be inaccurate, while too many may add overhead.
+ The lower the number, the less accurate `ThreadPool.workLoad` becomes,
+ but the higher the number, the more overhead there is. As a ballpark
+ estimate, aim for 10-100 iterations.
**/
public var workIterations(default, null):Tls = new Tls();
@@ -54,6 +53,7 @@ class WorkOutput
available on this target, `mode` will always be `SINGLE_THREADED`.
**/
public var mode(get, never):ThreadMode;
+
#if lime_threads
/**
__Set this only via the constructor.__
@@ -65,6 +65,7 @@ class WorkOutput
Messages sent by active jobs, received by the main thread.
**/
private var __jobOutput:Deque = new Deque();
+
/**
Thread-local storage. Tracks whether `sendError()` or `sendComplete()`
was called by this job.
@@ -77,6 +78,7 @@ class WorkOutput
Will be null in all other cases.
**/
public var activeJob(get, set):Null;
+
@:noCompletion private var __activeJob:Tls = new Tls();
private inline function new(mode:Null)
@@ -105,12 +107,11 @@ class WorkOutput
#if (lime_threads && html5)
if (mode == MULTI_THREADED)
{
- activeJob.doWork.makePortable();
- Thread.returnMessage(new ThreadEvent(COMPLETE, message, activeJob), transferList);
+ Thread.returnMessage({event: COMPLETE, message: message, jobID: activeJob.id}, transferList);
}
else
#end
- __jobOutput.add(new ThreadEvent(COMPLETE, message, activeJob));
+ __jobOutput.add({event: COMPLETE, message: message, jobID: activeJob.id});
}
}
@@ -130,12 +131,11 @@ class WorkOutput
#if (lime_threads && html5)
if (mode == MULTI_THREADED)
{
- activeJob.doWork.makePortable();
- Thread.returnMessage(new ThreadEvent(ERROR, message, activeJob), transferList);
+ Thread.returnMessage({event: ERROR, message: message, jobID: activeJob.id}, transferList);
}
else
#end
- __jobOutput.add(new ThreadEvent(ERROR, message, activeJob));
+ __jobOutput.add({event: ERROR, message: message, jobID: activeJob.id});
}
}
@@ -153,12 +153,11 @@ class WorkOutput
#if (lime_threads && html5)
if (mode == MULTI_THREADED)
{
- activeJob.doWork.makePortable();
- Thread.returnMessage(new ThreadEvent(PROGRESS, message, activeJob), transferList);
+ Thread.returnMessage({event: PROGRESS, message: message, jobID: activeJob.id}, transferList);
}
else
#end
- __jobOutput.add(new ThreadEvent(PROGRESS, message, activeJob));
+ __jobOutput.add({event: PROGRESS, message: message, jobID: activeJob.id});
}
}
@@ -174,7 +173,8 @@ class WorkOutput
var thread:Thread = Thread.create(executeThread);
#if html5
- thread.onMessage.add(function(event:ThreadEvent) {
+ thread.onMessage.add(function(event:ThreadEvent)
+ {
__jobOutput.add(event);
});
#end
@@ -198,6 +198,7 @@ class WorkOutput
{
return __activeJob.value;
}
+
private inline function set_activeJob(value:JobData):JobData
{
return __activeJob.value = value;
@@ -236,21 +237,18 @@ class WorkOutput
/**
A function that performs asynchronous work. This can either be work on
- another thread ("multi-threaded mode"), or it can represent a virtual
- thread ("single-threaded mode").
+ another thread ("multi-threaded mode"), or it can represent a green thread
+ ("single-threaded mode").
In single-threaded mode, the work function shouldn't complete the job all at
once, as the main thread would lock up. Instead, it should perform a
fraction of the job each time it's called. `ThreadPool` provides the
- function with a persistent `State` argument that can track progress.
- Alternatively, you may be able to bind your own `State` argument.
+ function with a persistent `State` argument for tracking progress, which can
+ be any object of your choice.
Caution: if using multi-threaded mode in HTML5, this must be a static
function and binding arguments is forbidden. Compile with
`-Dlime-warn-portability` to highlight functions that won't work.
-
- The exact length of `doWork` can vary, but single-threaded mode will run
- more smoothly if it's short enough to run several times per frame.
**/
#if (lime_threads && html5)
typedef WorkFunction = lime._internal.backend.html5.HTML5Thread.WorkFunction;
@@ -264,8 +262,8 @@ abstract WorkFunction(T) from T to T
{
switch (self.typeof().follow().toComplexType())
{
- case TPath({ sub: "WorkFunction", params: [TPType(t)] }):
- return macro ($self:$t)($a{args});
+ case TPath({sub: "WorkFunction", params: [TPType(t)]}):
+ return macro($self : $t)($a{args});
default:
throw "Underlying function type not found.";
}
@@ -278,8 +276,8 @@ abstract WorkFunction(T) from T to T
only accepts a single argument, you can pass multiple values as part of an
anonymous structure. (Or an array, or a class.)
- // Does not work: too many arguments.
- // threadPool.run(doWork, argument0, argument1, argument2);
+ // Does not work: too many arguments.
+ // threadPool.run(doWork, argument0, argument1, argument2);
// Works: all arguments are combined into one `State` object.
threadPool.run(doWork, { arg0: argument0, arg1: argument1, arg2: argument2 });
@@ -302,6 +300,7 @@ typedef State = Dynamic;
class JobData
{
private static var nextID:Int = 0;
+
/**
`JobData` instances will regularly be copied in HTML5, so checking
equality won't work. Instead, compare identifiers.
@@ -342,44 +341,25 @@ class JobData
}
#if haxe4 enum #else @:enum #end abstract ThreadEventType(String)
+
{
- /**
- Sent by the background thread, indicating completion.
- **/
+ // Events sent from a worker thread to the main thread
var COMPLETE = "COMPLETE";
- /**
- Sent by the background thread, indicating failure.
- **/
var ERROR = "ERROR";
- /**
- Sent by the background thread.
- **/
var PROGRESS = "PROGRESS";
- /**
- Sent by the main thread, indicating that the provided job should begin
- in place of any ongoing job. If `state == null`, the existing job will
- stop and the thread will go idle. (To run a job with no argument, set
- `state = {}` instead.)
- **/
+
+ // Commands sent from the main thread to a worker thread
var WORK = "WORK";
- /**
- Sent by the main thread to shut down a thread.
- **/
+ var CANCEL = "CANCEL";
var EXIT = "EXIT";
}
-class ThreadEvent
+typedef ThreadEvent =
{
- public var event(default, null):ThreadEventType;
- public var message(default, null):State;
- public var job(default, null):JobData;
-
- public inline function new(event:ThreadEventType, message:State, job:JobData)
- {
- this.event = event;
- this.message = message;
- this.job = job;
- }
+ var event:ThreadEventType;
+ @:optional var message:Dynamic;
+ @:optional var job:JobData;
+ @:optional var jobID:Int;
}
class JSAsync
@@ -403,7 +383,6 @@ class JSAsync
}
// Define platform-specific types
-
#if target.threaded
// Haxe 3 compatibility: "target.threaded" can't go in parentheses.
#elseif !(cpp || neko)
diff --git a/src/lime/text/Font.hx b/src/lime/text/Font.hx
index 52bde364f2..19843c2e8a 100644
--- a/src/lime/text/Font.hx
+++ b/src/lime/text/Font.hx
@@ -189,10 +189,11 @@ class Font
public function getGlyphs(characters:String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^`'\"/\\&*()[]{}<>|:;_-+=?,. "):Array
{
#if (lime_cffi && !macro)
- var glyphs:Dynamic = NativeCFFI.lime_font_get_glyph_indices(src, characters);
- // lime_font_get_glyph_indices returns Array
- // cast it to Array instead (Glyph is an abstract)
- return cast glyphs;
+ #if hl
+ return [for (index in NativeCFFI.lime_font_get_glyph_indices(src, characters)) new Glyph(index)];
+ #else
+ return NativeCFFI.lime_font_get_glyph_indices(src, characters);
+ #end
#else
return null;
#end
diff --git a/src/lime/tools/AndroidHelper.hx b/src/lime/tools/AndroidHelper.hx
index 54235eaaf3..e93a640bcc 100644
--- a/src/lime/tools/AndroidHelper.hx
+++ b/src/lime/tools/AndroidHelper.hx
@@ -8,8 +8,6 @@ class AndroidHelper
{
private static var adbName:String;
private static var adbPath:String;
- private static var androidName:String;
- private static var androidPath:String;
private static var emulatorName:String;
private static var emulatorPath:String;
@@ -159,30 +157,33 @@ class AndroidHelper
public static function initialize(project:HXProject):Void
{
- adbPath = project.environment.get("ANDROID_SDK") + "/tools/";
- androidPath = project.environment.get("ANDROID_SDK") + "/tools/";
- emulatorPath = project.environment.get("ANDROID_SDK") + "/tools/";
+ adbPath = project.environment.get("ANDROID_SDK") + "/platform-tools/";
+ emulatorPath = project.environment.get("ANDROID_SDK") + "/emulator/";
adbName = "adb";
- androidName = "android";
emulatorName = "emulator";
if (System.hostPlatform == WINDOWS)
{
adbName += ".exe";
- androidName += ".bat";
emulatorName += ".exe";
}
if (!FileSystem.exists(adbPath + adbName))
{
- adbPath = project.environment.get("ANDROID_SDK") + "/platform-tools/";
+ // in older SDKs, adb was located in /tools/
+ adbPath = project.environment.get("ANDROID_SDK") + "/tools/";
+ }
+
+ if (!FileSystem.exists(emulatorPath + emulatorName))
+ {
+ // in older SDKs, emulator was located in /tools/
+ emulatorPath = project.environment.get("ANDROID_SDK") + "/tools/";
}
if (System.hostPlatform != WINDOWS)
{
adbName = "./" + adbName;
- androidName = "./" + androidName;
emulatorName = "./" + emulatorName;
}
@@ -194,8 +195,18 @@ class AndroidHelper
public static function install(project:HXProject, targetPath:String, deviceID:String = null):String
{
+ if (!FileSystem.exists(adbPath + adbName))
+ {
+ Log.error("adb not found in Android SDK: " + project.environment.get("ANDROID_SDK"));
+ }
+
if (project.targetFlags.exists("emulator") || project.targetFlags.exists("simulator"))
{
+ if (!FileSystem.exists(emulatorPath + emulatorName))
+ {
+ Log.error("emulator not found in Android SDK: " + project.environment.get("ANDROID_SDK"));
+ }
+
Log.info("", "Searching for Android emulator");
var devices = listDevices();
@@ -280,16 +291,13 @@ class AndroidHelper
public static function listAVDs():Array
{
var avds = new Array();
- var output = System.runProcess(androidPath, androidName, ["list", "avd"]);
-
+ var output = System.runProcess(emulatorPath, emulatorName, ["-list-avds"]);
if (output != null && output != "")
{
+ // -list-avds returns only the avd names, separated by line breaks
for (line in output.split("\n"))
{
- if (line.indexOf("Name") > -1)
- {
- avds.push(StringTools.trim(line.substr(line.indexOf("Name") + 6)));
- }
+ avds.push(StringTools.trim(line));
}
}
@@ -343,6 +351,11 @@ class AndroidHelper
public static function trace(project:HXProject, debug:Bool, deviceID:String = null, customFilter:String = null):Void
{
+ if (!FileSystem.exists(adbPath + adbName))
+ {
+ Log.error("adb not found in Android SDK: " + project.environment.get("ANDROID_SDK"));
+ }
+
// Use -DFULL_LOGCAT or if you do not want to filter log messages
var args = ["logcat"];
@@ -395,6 +408,11 @@ class AndroidHelper
public static function uninstall(packageName:String, deviceID:String = null):Void
{
+ if (!FileSystem.exists(adbPath + adbName))
+ {
+ Log.error("adb not found in Android SDK");
+ }
+
var args = ["uninstall", packageName];
if (deviceID != null && deviceID != "")
diff --git a/src/lime/tools/Dependency.hx b/src/lime/tools/Dependency.hx
index 1136d89a3c..41a4bf7f7b 100644
--- a/src/lime/tools/Dependency.hx
+++ b/src/lime/tools/Dependency.hx
@@ -5,7 +5,7 @@ class Dependency
// TODO: Is "forceLoad" the best name? Implement "whole-archive" on GCC
public var embed:Bool;
public var forceLoad:Bool;
- public var webWorker:Bool;
+ public var allowWebWorkers:Bool;
public var name:String;
public var path:String;
diff --git a/src/lime/tools/HXProject.hx b/src/lime/tools/HXProject.hx
index 357cea03ef..26e90bdee3 100644
--- a/src/lime/tools/HXProject.hx
+++ b/src/lime/tools/HXProject.hx
@@ -189,6 +189,10 @@ class HXProject extends Script
else
{
environment = Sys.environment();
+ for (conflict in ["air", "android", "cpp", "flash", "hl", "html5", "ios", "linux", "mac", "neko", "webassembly", "windows"])
+ {
+ environment.remove(conflict);
+ }
}
haxedefs = new Map();
diff --git a/src/lime/tools/IOSHelper.hx b/src/lime/tools/IOSHelper.hx
index 8ba7bc3c1a..253ddc52c6 100644
--- a/src/lime/tools/IOSHelper.hx
+++ b/src/lime/tools/IOSHelper.hx
@@ -128,7 +128,7 @@ class IOSHelper
if (project.targetFlags.exists("simulator"))
{
- if (project.targetFlags.exists("i386") || project.targetFlags.exists("32"))
+ if (project.targetFlags.exists("i386") || project.targetFlags.exists("32") || project.targetFlags.exists("x86_32"))
{
commands.push("-arch");
commands.push("i386");
diff --git a/src/lime/tools/PlatformTarget.hx b/src/lime/tools/PlatformTarget.hx
index 87cf6e1d03..fe4ea957d6 100644
--- a/src/lime/tools/PlatformTarget.hx
+++ b/src/lime/tools/PlatformTarget.hx
@@ -4,6 +4,8 @@ import haxe.rtti.Meta;
import hxp.*;
import lime.tools.AssetHelper;
import lime.tools.CommandHelper;
+import sys.FileSystem;
+import sys.io.File;
class PlatformTarget
{
@@ -99,10 +101,12 @@ class PlatformTarget
if (command == "update" || command == "build" || command == "test")
{
logCommand("update");
- // #if lime
- // AssetHelper.processLibraries (project, targetDirectory);
- // #end
+
+ _touchedFiles = [];
update();
+
+ deleteStaleFiles(_touchedFiles);
+ _touchedFiles = null;
}
if (command == "build" || command == "test")
@@ -168,4 +172,85 @@ class PlatformTarget
@ignore public function update():Void {}
@ignore public function watch():Void {}
+
+ // Functions to track and delete stale files
+
+ /**
+ Files that were copied into the output directory due to something in
+ project.xml, but which might not be included next time.
+
+ `PlatformTarget` will handle assets and templates, but subclasses are
+ responsible for adding any other files they copy (e.g., dependencies).
+ **/
+ private var _touchedFiles:Array = null;
+
+ /**
+ Calls `System.copyIfNewer()` with the given arguments, then records the
+ file in `_touchedFiles`. See `_touchedFiles` for information about what
+ needs to be recorded.
+ **/
+ private function copyIfNewer(source:String, destination:String):Void
+ {
+ System.copyIfNewer(source, destination);
+
+ if (_touchedFiles != null)
+ {
+ _touchedFiles.push(destination);
+ }
+ }
+
+ private function deleteStaleFiles(touchedFiles:Array):Void
+ {
+ if (project.defines.exists("lime-ignore-stale-files")) return;
+
+ for (asset in project.assets)
+ {
+ touchedFiles.push(targetDirectory + "/bin/" + asset.targetPath);
+ }
+
+ var record:String = targetDirectory + "/.files";
+ if (FileSystem.exists(record))
+ {
+ for (oldFile in File.getContent(record).split("\n"))
+ {
+ if (oldFile.length > 0 && touchedFiles.indexOf(oldFile) < 0)
+ {
+ System.deleteFile(oldFile);
+ }
+ }
+ }
+
+ File.saveContent(record, touchedFiles.join("\n"));
+ }
+
+ /**
+ Calls `System.recursiveCopy()` with the given arguments, then records
+ the files in `_touchedFiles`. See `_touchedFiles` for information about
+ what needs to be recorded.
+ **/
+ private function recursiveCopy(source:String, destination:String, context:Dynamic = null, process:Bool = true):Void
+ {
+ System.recursiveCopy(source, destination, context, process);
+
+ if (_touchedFiles == null || !FileSystem.exists(source)) return;
+
+ function recurse(source:String, destination:String):Void
+ {
+ for (file in FileSystem.readDirectory(source))
+ {
+ if (file.charAt(0) == ".") continue;
+
+ if (FileSystem.isDirectory(source + "/" + file))
+ {
+ recurse(source + "/" + file, destination + "/" + file);
+ }
+ else
+ {
+ _touchedFiles.push(destination + "/" + file);
+ }
+ }
+ }
+
+ recurse(source, destination);
+ }
}
diff --git a/src/lime/tools/ProjectXMLParser.hx b/src/lime/tools/ProjectXMLParser.hx
index d815d43762..73cc941a22 100644
--- a/src/lime/tools/ProjectXMLParser.hx
+++ b/src/lime/tools/ProjectXMLParser.hx
@@ -894,7 +894,10 @@ class ProjectXMLParser extends HXProject
Log.error(substitute(element.att.value));
case "echo":
- Log.println(substitute(element.att.value));
+ if (command != "display")
+ {
+ Log.println(substitute(element.att.value));
+ }
case "log":
var verbose = "";
@@ -908,21 +911,24 @@ class ProjectXMLParser extends HXProject
{
Log.error(substitute(element.att.error), verbose);
}
- else if (element.has.warn)
- {
- Log.warn(substitute(element.att.warn), verbose);
- }
- else if (element.has.info)
- {
- Log.info(substitute(element.att.info), verbose);
- }
- else if (element.has.value)
- {
- Log.info(substitute(element.att.value), verbose);
- }
- else if (verbose != "")
+ else if (command != "display")
{
- Log.info("", verbose);
+ if (element.has.warn)
+ {
+ Log.warn(substitute(element.att.warn), verbose);
+ }
+ else if (element.has.info)
+ {
+ Log.info(substitute(element.att.info), verbose);
+ }
+ else if (element.has.value)
+ {
+ Log.info(substitute(element.att.value), verbose);
+ }
+ else if (verbose != "")
+ {
+ Log.info("", verbose);
+ }
}
case "path":
@@ -1662,6 +1668,11 @@ class ProjectXMLParser extends HXProject
dependency.forceLoad = parseBool(element.att.resolve("force-load"));
}
+ if (element.has.resolve("allow-web-workers"))
+ {
+ dependency.allowWebWorkers = parseBool(element.att.resolve("allow-web-workers"));
+ }
+
var i = dependencies.length;
while (i-- > 0)
diff --git a/svg.n b/svg.n
index e86d7ef83a..a1eac4b05f 100644
Binary files a/svg.n and b/svg.n differ
diff --git a/templates/android/template/app/build.gradle b/templates/android/template/app/build.gradle
index d2e5260d63..f6b9fad10a 100644
--- a/templates/android/template/app/build.gradle
+++ b/templates/android/template/app/build.gradle
@@ -13,6 +13,7 @@ System.setProperty('java.awt.headless','false')
} */
android {
+ namespace "::APP_PACKAGE::"
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
::if (ANDROID_GRADLE_PLUGIN>="4.0")::ndkPath '::ANDROID_NDK_ROOT_ESCAPED::'::end::
diff --git a/templates/android/template/app/src/main/AndroidManifest.xml b/templates/android/template/app/src/main/AndroidManifest.xml
index 501db19f78..aeac9518c8 100644
--- a/templates/android/template/app/src/main/AndroidManifest.xml
+++ b/templates/android/template/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
@@ -25,6 +25,16 @@
+ ::foreach ANDROID_ACCEPT_FILE_INTENT::
+
+
+
+
+
+
+
+ ::end::
+
diff --git a/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java b/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java
index e0c37f44cb..3c28d7a6cb 100644
--- a/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java
+++ b/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java
@@ -8,6 +8,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -29,10 +30,10 @@ public class GameActivity extends SDLActivity {
private static AssetManager assetManager;
private static List extensions;
private static DisplayMetrics metrics;
+ private static Vibrator vibrator;
public Handler handler;
-
public static double getDisplayXDPI () {
if (metrics == null) {
@@ -109,6 +110,7 @@ protected void onCreate (Bundle state) {
super.onCreate (state);
assetManager = getAssets ();
+ vibrator = (Vibrator)mSingleton.getSystemService (Context.VIBRATOR_SERVICE);
handler = new Handler ();
Extension.assetManager = assetManager;
@@ -176,6 +178,12 @@ protected void onCreate (Bundle state) {
@Override protected void onPause () {
+ if (vibrator != null) {
+
+ vibrator.cancel ();
+
+ }
+
super.onPause ();
for (Extension extension : extensions) {
@@ -370,25 +378,47 @@ public static void postUICallback (final long handle) {
public static void vibrate (int period, int duration) {
- Vibrator v = (Vibrator)mSingleton.getSystemService (Context.VIBRATOR_SERVICE);
+ if (vibrator == null || !vibrator.hasVibrator () || period < 0 || duration <= 0) {
+
+ return;
+
+ }
if (period == 0) {
- v.vibrate (duration);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+
+ vibrator.vibrate (VibrationEffect.createOneShot (duration, VibrationEffect.DEFAULT_AMPLITUDE));
+
+ } else {
+
+ vibrator.vibrate (duration);
+
+ }
} else {
- int periodMS = (int)Math.ceil (period / 2);
- int count = (int)Math.ceil ((duration / period) * 2);
+ // each period has two halves (vibrator off/vibrator on), and each half requires a separate entry in the array
+ int periodMS = (int)Math.ceil (period / 2.0);
+ int count = (int)Math.ceil (duration / (double) periodMS);
long[] pattern = new long[count];
- for (int i = 0; i < count; i++) {
+ // the first entry is the delay before vibration starts, so leave it as 0
+ for (int i = 1; i < count; i++) {
pattern[i] = periodMS;
}
- v.vibrate (pattern, -1);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+
+ vibrator.vibrate (VibrationEffect.createWaveform (pattern, -1));
+
+ } else {
+
+ vibrator.vibrate (pattern, -1);
+
+ }
}
diff --git a/templates/cpp/static/BuildMain.xml b/templates/cpp/static/BuildMain.xml
index ecc5071300..1342c8a9b0 100644
--- a/templates/cpp/static/BuildMain.xml
+++ b/templates/cpp/static/BuildMain.xml
@@ -63,6 +63,7 @@
+
diff --git a/templates/extension/dependencies/android/build.gradle b/templates/extension/dependencies/android/build.gradle
index 8b5014f91c..40f326da80 100644
--- a/templates/extension/dependencies/android/build.gradle
+++ b/templates/extension/dependencies/android/build.gradle
@@ -12,6 +12,7 @@ buildscript {
apply plugin: 'com.android.library'
android {
+ namespace "org.haxe.extension.::extensionLowerCase::"
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
}
diff --git a/templates/extension/dependencies/android/src/main/AndroidManifest.xml b/templates/extension/dependencies/android/src/main/AndroidManifest.xml
index 698a1e3ce7..b2d3ea1235 100644
--- a/templates/extension/dependencies/android/src/main/AndroidManifest.xml
+++ b/templates/extension/dependencies/android/src/main/AndroidManifest.xml
@@ -1,6 +1,2 @@
-
-
-
-
-
\ No newline at end of file
+
diff --git a/templates/html5/output.js b/templates/html5/output.js
index ad3aac5d49..da522b6ca3 100644
--- a/templates/html5/output.js
+++ b/templates/html5/output.js
@@ -1,38 +1,43 @@
-(function ($hx_exports, $global) { "use strict"; var $hx_script = (function (exports, global) { ::SOURCE_FILE::
-});
-::if false::
- If `window` is undefined, it means this script is running as a web worker.
- In that case, there's no need for exports, and all we need to do is run the
- static initializers.
-::end::if(typeof window == "undefined") {
- $hx_script({}, $global);
-} else {
- $hx_exports.lime = $hx_exports.lime || {};
- $hx_exports.lime.$scripts = $hx_exports.lime.$scripts || {};
- $hx_exports.lime.$scripts["::APP_FILE::"] = $hx_script;
- $hx_exports.lime.embed = function(projectName) { var exports = {};
- var script = $hx_exports.lime.$scripts[projectName];
- if (!script) throw Error("Cannot find project name \"" + projectName + "\"");
- script(exports, $global);
- for (var key in exports) $hx_exports[key] = $hx_exports[key] || exports[key];
- var lime = exports.lime || window.lime;
- if (lime && lime.embed && this != lime.embed) lime.embed.apply(lime, arguments);
- return exports;
- };
-}
-::if false::
- AMD compatibility: If define() is present we need to
- - call it, to define our module
- - disable it so that the embedded libraries register themselves in the global scope!
-::end::if(typeof define == "function" && define.amd) {
- define([], function() { return $hx_exports.lime; });
- define.__amd = define.amd;
- define.amd = null;
-}
-})(typeof exports != "undefined" ? exports : typeof define == "function" && define.amd ? {} : typeof window != "undefined" ? window : typeof self != "undefined" ? self : this, typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);
-::if embeddedLibraries::::foreach (embeddedLibraries)::
+var $lime_init = (function ($hx_exports, $global) {
+ "use strict";
+
+ var $hx_script = (function (exports, global) {
+::SOURCE_FILE::
+ });
+
+ if (typeof self !== "undefined" && self.constructor.name.includes("Worker")) {
+ // No need for exports in a worker context, just initialize statics.
+ $hx_script({}, $global);
+ } else {
+ $hx_exports.lime = $hx_exports.lime || {};
+ $hx_exports.lime.$scripts = $hx_exports.lime.$scripts || {};
+ $hx_exports.lime.$scripts["::APP_FILE::"] = $hx_script;
+ $hx_exports.lime.embed = function (projectName) {
+ var exports = {};
+ var script = $hx_exports.lime.$scripts[projectName];
+ if (!script) throw Error("Cannot find project name \"" + projectName + "\"");
+ script(exports, $global);
+ for (var key in exports) $hx_exports[key] = $hx_exports[key] || exports[key];
+ var lime = exports.lime || window.lime;
+ if (lime && lime.embed && this !== lime.embed) lime.embed.apply(lime, arguments);
+ return exports;
+ };
+ }
+
+ if (typeof define === "function" && define.amd) {
+ define([], function () { return $hx_exports.lime; });
+ define.__amd = define.amd;
+ define.amd = null;
+ }
+})
+
+$lime_init(typeof exports !== "undefined" ? exports : typeof define === "function" && define.amd ? {} : typeof window !== "undefined" ? window : typeof self !== "undefined" ? self : this,
+typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : this);
+
+::if embeddedLibraries::::foreach embeddedLibraries::
::__current__::::end::::end::
-if(typeof define == "function" && define.__amd) {
+
+if (typeof define === "function" && define.__amd) {
define.amd = define.__amd;
delete define.__amd;
-}
\ No newline at end of file
+}
diff --git a/tools/CommandLineTools.hx b/tools/CommandLineTools.hx
index 07886b242f..9bd280db84 100644
--- a/tools/CommandLineTools.hx
+++ b/tools/CommandLineTools.hx
@@ -485,15 +485,14 @@ class CommandLineTools
}
case MAC:
- // if (System.hostArchitecture == X64) {
-
- untyped $loader.path = $array(path + "Mac64/", $loader.path);
-
- // } else {
-
- // untyped $loader.path = $array (path + "Mac/", $loader.path);
-
- // }
+ if (System.hostArchitecture == X64)
+ {
+ untyped $loader.path = $array(path + "Mac64/", $loader.path);
+ }
+ else if (System.hostArchitecture == ARM64)
+ {
+ untyped $loader.path = $array(path + "MacArm64/", $loader.path);
+ }
case LINUX:
var arguments = Sys.args();
@@ -998,12 +997,12 @@ class CommandLineTools
if (isBuildCommand)
{
Log.println(" \x1b[3m(windows|mac|linux|android)\x1b[0m \x1b[1m-static\x1b[0m -- Compile as a static C++ executable");
- Log.println(" \x1b[3m(windows|mac|linux)\x1b[0m \x1b[1m-32\x1b[0m -- Compile for 32-bit instead of the OS default");
- Log.println(" \x1b[3m(windows|mac|linux)\x1b[0m \x1b[1m-64\x1b[0m -- Compile for 64-bit instead of the OS default");
+ Log.println(" \x1b[3m(windows|mac|linux)\x1b[0m \x1b[1m-x86_32\x1b[0m -- Compile for x86_32 instead of the OS default");
+ Log.println(" \x1b[3m(windows|mac|linux)\x1b[0m \x1b[1m-x86_64\x1b[0m -- Compile for x86_64 instead of the OS default");
Log.println(" \x1b[3m(ios|android)\x1b[0m \x1b[1m-armv6\x1b[0m -- Compile for ARMv6 instead of the OS defaults");
Log.println(" \x1b[3m(ios|android)\x1b[0m \x1b[1m-armv7\x1b[0m -- Compile for ARMv7 instead of the OS defaults");
Log.println(" \x1b[3m(ios|android)\x1b[0m \x1b[1m-armv7s\x1b[0m -- Compile for ARMv7s instead of the OS defaults");
- Log.println(" \x1b[3m(ios)\x1b[0m \x1b[1m-arm64\x1b[0m -- Compile for ARM64 instead of the OS defaults");
+ Log.println(" \x1b[3m(mac|ios|android)\x1b[0m \x1b[1m-arm64\x1b[0m -- Compile for ARM64 instead of the OS defaults");
Log.println(" \x1b[3m(ios)\x1b[0m \x1b[1m-nosign\x1b[0m -- Compile executable, but skip codesigning");
}
@@ -2241,11 +2240,11 @@ class CommandLineTools
overrides.architectures.push(value);
}
}
- else if (argument == "-64")
+ else if (argument == "-64" || argument == "-x86_64")
{
overrides.architectures.push(Architecture.X64);
}
- else if (argument == "-32")
+ else if (argument == "-32" || argument == "-x86_32")
{
overrides.architectures.push(Architecture.X86);
}
diff --git a/tools/RunScript.hx b/tools/RunScript.hx
index 0f4243a155..ec93140187 100644
--- a/tools/RunScript.hx
+++ b/tools/RunScript.hx
@@ -35,7 +35,7 @@ class RunScript
if (!rebuildBinaries) return;
- var platforms = ["Windows", "Mac", "Mac64", "Linux", "Linux64"];
+ var platforms = ["Windows", "Mac", "Mac64", "MacArm64", "Linux", "Linux64"];
for (platform in platforms)
{
@@ -64,7 +64,7 @@ class RunScript
System.runCommand(limeDirectory, "neko", args.concat(["windows", toolsDirectory]));
}
- case "Mac", "Mac64":
+ case "Mac", "Mac64", "MacArm64":
if (System.hostPlatform == MAC)
{
System.runCommand(limeDirectory, "neko", args.concat(["mac", toolsDirectory]));
diff --git a/tools/SVGExport.hx b/tools/SVGExport.hx
index c11c9774d5..94b442337d 100644
--- a/tools/SVGExport.hx
+++ b/tools/SVGExport.hx
@@ -67,8 +67,14 @@ class SVGExport
// }
case MAC:
- untyped $loader.path = $array(path + "Mac/", $loader.path);
- untyped $loader.path = $array(path + "Mac64/", $loader.path);
+ if (System.hostArchitecture == X64)
+ {
+ untyped $loader.path = $array(path + "Mac64/", $loader.path);
+ }
+ else if (System.hostArchitecture == ARM64)
+ {
+ untyped $loader.path = $array(path + "MacArm64/", $loader.path);
+ }
case LINUX:
var arguments = Sys.args();
diff --git a/tools/platforms/AndroidPlatform.hx b/tools/platforms/AndroidPlatform.hx
index 576f1d9fec..4d4caaaf12 100644
--- a/tools/platforms/AndroidPlatform.hx
+++ b/tools/platforms/AndroidPlatform.hx
@@ -481,7 +481,7 @@ class AndroidPlatform extends PlatformTarget
context.ANDROID_APPLICATION = project.config.getKeyValueArray("android.application", {
"android:label": project.meta.title,
"android:allowBackup": "true",
- "android:theme": "@android:style/Theme.NoTitleBar" + (project.window.fullscreen ? ".Fullscreen" : null),
+ "android:theme": "@android:style/Theme.NoTitleBar" + (project.window.fullscreen ? ".Fullscreen" : ""),
"android:hardwareAccelerated": "true",
"android:allowNativeHeapPointerTagging": context.ANDROID_TARGET_SDK_VERSION >= 30 ? "false" : null
});
@@ -490,9 +490,12 @@ class AndroidPlatform extends PlatformTarget
"android:exported": "true",
"android:launchMode": "singleTask",
"android:label": project.meta.title,
- "android:configChanges": project.config.getArrayString("android.configChanges", ["keyboardHidden", "orientation", "screenSize", "screenLayout", "uiMode"]).join("|"),
+ "android:configChanges": project.config.getArrayString("android.configChanges",
+ ["layoutDirection", "locale", "orientation", "uiMode", "screenLayout", "screenSize", "smallestScreenSize", "keyboard", "keyboardHidden", "navigation"])
+ .join("|"),
"android:screenOrientation": project.window.orientation == PORTRAIT ? "sensorPortrait" : (project.window.orientation == LANDSCAPE ? "sensorLandscape" : null)
});
+ context.ANDROID_ACCEPT_FILE_INTENT = project.config.getArrayString("android.accept-file-intent", []);
if (!project.environment.exists("ANDROID_SDK") || !project.environment.exists("ANDROID_NDK_ROOT"))
{
@@ -595,17 +598,17 @@ class AndroidPlatform extends PlatformTarget
{
if (FileSystem.isDirectory(javaPath))
{
- System.recursiveCopy(javaPath, sourceSet + "/java", context, true);
+ recursiveCopy(javaPath, sourceSet + "/java", context, true);
}
else
{
if (Path.extension(javaPath) == "jar")
{
- System.copyIfNewer(javaPath, destination + "/app/libs/" + Path.withoutDirectory(javaPath));
+ copyIfNewer(javaPath, destination + "/app/libs/" + Path.withoutDirectory(javaPath));
}
else
{
- System.copyIfNewer(javaPath, sourceSet + "/java/" + Path.withoutDirectory(javaPath));
+ copyIfNewer(javaPath, sourceSet + "/java/" + Path.withoutDirectory(javaPath));
}
}
}
@@ -618,7 +621,7 @@ class AndroidPlatform extends PlatformTarget
for (library in cast(context.ANDROID_LIBRARY_PROJECTS, Array))
{
- System.recursiveCopy(library.source, destination + "/deps/" + library.name, context, true);
+ recursiveCopy(library.source, destination + "/deps/" + library.name, context, true);
}
ProjectHelper.recursiveSmartCopyTemplate(project, "android/template", destination, context);
diff --git a/tools/platforms/HTML5Platform.hx b/tools/platforms/HTML5Platform.hx
index 94d8a45f20..a5bbaf76b3 100644
--- a/tools/platforms/HTML5Platform.hx
+++ b/tools/platforms/HTML5Platform.hx
@@ -159,9 +159,9 @@ class HTML5Platform extends PlatformTarget
if (dependency.embed && StringTools.endsWith(dependency.path, ".js") && FileSystem.exists(dependency.path))
{
var script = File.getContent(dependency.path);
- if (!dependency.webWorker)
+ if (!dependency.allowWebWorkers)
{
- script = 'if(typeof window != "undefined") {\n' + script + "\n}";
+ script = 'if(typeof self === "undefined" || !self.constructor.name.includes("Worker")) { $script }';
}
context.embeddedLibraries.push(script);
}
@@ -438,7 +438,7 @@ class HTML5Platform extends PlatformTarget
var name = Path.withoutDirectory(dependency.path);
context.linkedLibraries.push("./" + dependencyPath + "/" + name);
- System.copyIfNewer(dependency.path, Path.combine(destination, Path.combine(dependencyPath, name)));
+ copyIfNewer(dependency.path, Path.combine(destination, Path.combine(dependencyPath, name)));
}
}
}
diff --git a/tools/platforms/IOSPlatform.hx b/tools/platforms/IOSPlatform.hx
index dc8e9472e4..e13a9cdffd 100644
--- a/tools/platforms/IOSPlatform.hx
+++ b/tools/platforms/IOSPlatform.hx
@@ -862,7 +862,7 @@ class IOSPlatform extends PlatformTarget
fileName = "lib" + fileName;
}
- System.copyIfNewer(dependency.path, projectDirectory + "/lib/" + arch + "/" + fileName);
+ copyIfNewer(dependency.path, projectDirectory + "/lib/" + arch + "/" + fileName);
}
}
}
diff --git a/tools/platforms/LinuxPlatform.hx b/tools/platforms/LinuxPlatform.hx
index d6af573148..e08ed38742 100644
--- a/tools/platforms/LinuxPlatform.hx
+++ b/tools/platforms/LinuxPlatform.hx
@@ -120,7 +120,7 @@ class LinuxPlatform extends PlatformTarget
for (architecture in project.architectures)
{
- if (!targetFlags.exists("32") && architecture == Architecture.X64)
+ if (!targetFlags.exists("32") && !targetFlags.exists("x86_32") && architecture == Architecture.X64)
{
is64 = true;
}
@@ -507,12 +507,12 @@ class LinuxPlatform extends PlatformTarget
}
else
{
- if (!targetFlags.exists("32") && System.hostArchitecture == X64)
+ if (!targetFlags.exists("32") && !targetFlags.exists("x86_32") && System.hostArchitecture == X64)
{
commands.push(["-Dlinux", "-DHXCPP_M64"]);
}
- if (!targetFlags.exists("64") && (command == "rebuild" || System.hostArchitecture == X86))
+ if (!targetFlags.exists("64") && !targetFlags.exists("x86_64") && (command == "rebuild" || System.hostArchitecture == X86))
{
commands.push(["-Dlinux", "-DHXCPP_M32"]);
}
diff --git a/tools/platforms/MacPlatform.hx b/tools/platforms/MacPlatform.hx
index 45865cdacd..37cc8639ed 100644
--- a/tools/platforms/MacPlatform.hx
+++ b/tools/platforms/MacPlatform.hx
@@ -423,7 +423,11 @@ class MacPlatform extends PlatformTarget
// TODO: Support single binary
commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_M64", "-Dhashlink"]);
}
- else if (!targetFlags.exists("32"))
+ else if (targetFlags.exists("arm64"))
+ {
+ commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_ARM64"]);
+ }
+ else if (!targetFlags.exists("32") && !targetFlags.exists("x86_32"))
{
commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_M64"]);
}
@@ -434,7 +438,19 @@ class MacPlatform extends PlatformTarget
case X86:
commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_M32"]);
case ARM64:
- commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_ARM64"]);
+ if (targetFlags.exists("hl"))
+ {
+ // hashlink doesn't support arm64 macs yet
+ commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_ARCH=x86_64", "-Dhashlink"]);
+ }
+ else if (targetFlags.exists("64") || targetFlags.exists("x86_64"))
+ {
+ commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_ARCH=x86_64"]);
+ }
+ else
+ {
+ commands.push(["-Dmac", "-DHXCPP_CLANG", "-DHXCPP_ARM64"]);
+ }
default:
}
@@ -584,7 +600,12 @@ class MacPlatform extends PlatformTarget
private inline function get_dirSuffix():String
{
- return targetArchitecture == X64 ? "64" : "";
+ if (targetFlags.exists("hl"))
+ {
+ // hashlink doesn't support arm64 macs yet
+ return "64";
+ }
+ return targetArchitecture == X64 ? "64" : targetArchitecture == ARM64 ? "Arm64" : "";
}
/**
@@ -710,23 +731,13 @@ class MacPlatform extends PlatformTarget
if (isLibrary)
{
var newId = "@executable_path/" + fileName;
- var process = new Process("install_name_tool", ["-id", newId, absoluteFilePath]);
- var exitCode = process.exitCode(true);
- if (exitCode != 0)
- {
- Log.error('install_name_tool -id process exited with code: <${exitCode}> for file <${fileName}>');
- }
+ System.runCommand("", "install_name_tool", ["-id", newId, absoluteFilePath]);
}
for (homebrewPath in homebrewDependencyPaths)
{
var newPath = "@executable_path/" + Path.withoutDirectory(homebrewPath);
- var process = new Process("install_name_tool", ["-change", homebrewPath, newPath, absoluteFilePath]);
- var exitCode = process.exitCode(true);
- if (exitCode != 0)
- {
- Log.error('install_name_tool -change process exited with code: <${exitCode}> for file <${Path.withoutDirectory(homebrewPath)}>');
- }
+ System.runCommand("", "install_name_tool", ["-change", homebrewPath, newPath, absoluteFilePath]);
}
}
}
diff --git a/tools/platforms/TVOSPlatform.hx b/tools/platforms/TVOSPlatform.hx
index e0273b3b3a..76714971c7 100644
--- a/tools/platforms/TVOSPlatform.hx
+++ b/tools/platforms/TVOSPlatform.hx
@@ -633,7 +633,7 @@ class TVOSPlatform extends PlatformTarget
fileName = "lib" + fileName;
}
- System.copyIfNewer(dependency.path, projectDirectory + "/lib/" + arch + "/" + fileName);
+ copyIfNewer(dependency.path, projectDirectory + "/lib/" + arch + "/" + fileName);
}
}
}
diff --git a/tools/platforms/WebAssemblyPlatform.hx b/tools/platforms/WebAssemblyPlatform.hx
index a8130365c7..0674c45b11 100644
--- a/tools/platforms/WebAssemblyPlatform.hx
+++ b/tools/platforms/WebAssemblyPlatform.hx
@@ -471,7 +471,7 @@ class WebAssemblyPlatform extends PlatformTarget
var name = Path.withoutDirectory(dependency.path);
context.linkedLibraries.push("./" + dependencyPath + "/" + name);
- System.copyIfNewer(dependency.path, Path.combine(destination, Path.combine(dependencyPath, name)));
+ copyIfNewer(dependency.path, Path.combine(destination, Path.combine(dependencyPath, name)));
}
}
}
diff --git a/tools/platforms/WindowsPlatform.hx b/tools/platforms/WindowsPlatform.hx
index 416e5d7da1..d1babe54f7 100644
--- a/tools/platforms/WindowsPlatform.hx
+++ b/tools/platforms/WindowsPlatform.hx
@@ -145,7 +145,7 @@ class WindowsPlatform extends PlatformTarget
else if (project.targetFlags.exists("hl") || targetFlags.exists("hlc"))
{
targetType = "hl";
- is64 = !project.flags.exists("32");
+ is64 = !project.flags.exists("32") && !project.flags.exists("x86_32");
}
else if (project.targetFlags.exists("cppia"))
{
@@ -286,7 +286,7 @@ class WindowsPlatform extends PlatformTarget
if (StringTools.endsWith(dependency.path, ".dll"))
{
var fileName = Path.withoutDirectory(dependency.path);
- System.copyIfNewer(dependency.path, applicationDirectory + "/" + fileName);
+ copyIfNewer(dependency.path, applicationDirectory + "/" + fileName);
}
}
@@ -737,7 +737,8 @@ class WindowsPlatform extends PlatformTarget
if (targetType == "hl")
{
// default to 64 bit, just like upstream Hashlink releases
- if (!targetFlags.exists("32") && (System.hostArchitecture == X64 || targetFlags.exists("64")))
+ if (!targetFlags.exists("32") && !targetFlags.exists("x86_32")
+ && (System.hostArchitecture == X64 || targetFlags.exists("64") || targetFlags.exists("x86_64")))
{
commands.push(["-Dwindows", "-DHXCPP_M64", "-Dhashlink"]);
}
@@ -748,7 +749,7 @@ class WindowsPlatform extends PlatformTarget
}
else
{
- if (!targetFlags.exists("64")
+ if (!targetFlags.exists("64") && !targetFlags.exists("x86_64")
&& (command == "rebuild" || System.hostArchitecture == X86 || (targetType != "cpp" && targetType != "winrt")))
{
if (targetType == "winrt")
@@ -765,7 +766,7 @@ class WindowsPlatform extends PlatformTarget
// as previous Windows builds. For now, force -64 to be done last
// so that it can be debugged in a default "rebuild"
- if (!targetFlags.exists("32")
+ if (!targetFlags.exists("32") && !targetFlags.exists("x86_32")
&& System.hostArchitecture == X64
&& (command != "rebuild" || targetType == "cpp" || targetType == "winrt"))
{
@@ -1121,7 +1122,7 @@ class WindowsPlatform extends PlatformTarget
var name = Path.withoutDirectory(dependency.path);
context.linkedLibraries.push("./js/lib/" + name);
- System.copyIfNewer(dependency.path, Path.combine(destination, Path.combine("js/lib", name)));
+ copyIfNewer(dependency.path, Path.combine(destination, Path.combine("js/lib", name)));
}
}