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))); } }