Ticket #473: html5-v2.patch
File html5-v2.patch, 55.2 KB (added by , 8 years ago) |
---|
-
include/inflate.min.js
1 /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function m(b){throw b;}var n=void 0,r=this;function s(b,d){var a=b.split("."),c=r;!(a[0]in c)&&c.execScript&&c.execScript("var "+a[0]);for(var f;a.length&&(f=a.shift());)!a.length&&d!==n?c[f]=d:c=c[f]?c[f]:c[f]={}};var u="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array;function v(b){var d=b.length,a=0,c=Number.POSITIVE_INFINITY,f,e,g,h,k,l,q,p,t;for(p=0;p<d;++p)b[p]>a&&(a=b[p]),b[p]<c&&(c=b[p]);f=1<<a;e=new (u?Uint32Array:Array)(f);g=1;h=0;for(k=2;g<=a;){for(p=0;p<d;++p)if(b[p]===g){l=0;q=h;for(t=0;t<g;++t)l=l<<1|q&1,q>>=1;for(t=l;t<f;t+=k)e[t]=g<<16|p;++h}++g;h<<=1;k<<=1}return[e,a,c]};function w(b,d){this.g=[];this.h=32768;this.d=this.f=this.a=this.l=0;this.input=u?new Uint8Array(b):b;this.m=!1;this.i=x;this.r=!1;if(d||!(d={}))d.index&&(this.a=d.index),d.bufferSize&&(this.h=d.bufferSize),d.bufferType&&(this.i=d.bufferType),d.resize&&(this.r=d.resize);switch(this.i){case y:this.b=32768;this.c=new (u?Uint8Array:Array)(32768+this.h+258);break;case x:this.b=0;this.c=new (u?Uint8Array:Array)(this.h);this.e=this.z;this.n=this.v;this.j=this.w;break;default:m(Error("invalid inflate mode"))}} 2 var y=0,x=1,z={t:y,s:x}; 3 w.prototype.k=function(){for(;!this.m;){var b=A(this,3);b&1&&(this.m=!0);b>>>=1;switch(b){case 0:var d=this.input,a=this.a,c=this.c,f=this.b,e=n,g=n,h=n,k=c.length,l=n;this.d=this.f=0;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (first byte)"));g=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (second byte)"));g|=e<<8;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (first byte)"));h=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (second byte)"));h|= 4 e<<8;g===~h&&m(Error("invalid uncompressed block header: length verify"));a+g>d.length&&m(Error("input buffer is broken"));switch(this.i){case y:for(;f+g>c.length;){l=k-f;g-=l;if(u)c.set(d.subarray(a,a+l),f),f+=l,a+=l;else for(;l--;)c[f++]=d[a++];this.b=f;c=this.e();f=this.b}break;case x:for(;f+g>c.length;)c=this.e({p:2});break;default:m(Error("invalid inflate mode"))}if(u)c.set(d.subarray(a,a+g),f),f+=g,a+=g;else for(;g--;)c[f++]=d[a++];this.a=a;this.b=f;this.c=c;break;case 1:this.j(B,C);break;case 2:aa(this); 5 break;default:m(Error("unknown BTYPE: "+b))}}return this.n()}; 6 var D=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],E=u?new Uint16Array(D):D,F=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],G=u?new Uint16Array(F):F,H=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],I=u?new Uint8Array(H):H,J=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],K=u?new Uint16Array(J):J,L=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13, 7 13],M=u?new Uint8Array(L):L,N=new (u?Uint8Array:Array)(288),O,P;O=0;for(P=N.length;O<P;++O)N[O]=143>=O?8:255>=O?9:279>=O?7:8;var B=v(N),Q=new (u?Uint8Array:Array)(30),R,S;R=0;for(S=Q.length;R<S;++R)Q[R]=5;var C=v(Q);function A(b,d){for(var a=b.f,c=b.d,f=b.input,e=b.a,g;c<d;)g=f[e++],g===n&&m(Error("input buffer is broken")),a|=g<<c,c+=8;g=a&(1<<d)-1;b.f=a>>>d;b.d=c-d;b.a=e;return g} 8 function T(b,d){for(var a=b.f,c=b.d,f=b.input,e=b.a,g=d[0],h=d[1],k,l,q;c<h;){k=f[e++];if(k===n)break;a|=k<<c;c+=8}l=g[a&(1<<h)-1];q=l>>>16;b.f=a>>q;b.d=c-q;b.a=e;return l&65535} 9 function aa(b){function d(a,b,c){var d,e,f,g;for(g=0;g<a;)switch(d=T(this,b),d){case 16:for(f=3+A(this,2);f--;)c[g++]=e;break;case 17:for(f=3+A(this,3);f--;)c[g++]=0;e=0;break;case 18:for(f=11+A(this,7);f--;)c[g++]=0;e=0;break;default:e=c[g++]=d}return c}var a=A(b,5)+257,c=A(b,5)+1,f=A(b,4)+4,e=new (u?Uint8Array:Array)(E.length),g,h,k,l;for(l=0;l<f;++l)e[E[l]]=A(b,3);g=v(e);h=new (u?Uint8Array:Array)(a);k=new (u?Uint8Array:Array)(c);b.j(v(d.call(b,a,g,h)),v(d.call(b,c,g,k)))} 10 w.prototype.j=function(b,d){var a=this.c,c=this.b;this.o=b;for(var f=a.length-258,e,g,h,k;256!==(e=T(this,b));)if(256>e)c>=f&&(this.b=c,a=this.e(),c=this.b),a[c++]=e;else{g=e-257;k=G[g];0<I[g]&&(k+=A(this,I[g]));e=T(this,d);h=K[e];0<M[e]&&(h+=A(this,M[e]));c>=f&&(this.b=c,a=this.e(),c=this.b);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c}; 11 w.prototype.w=function(b,d){var a=this.c,c=this.b;this.o=b;for(var f=a.length,e,g,h,k;256!==(e=T(this,b));)if(256>e)c>=f&&(a=this.e(),f=a.length),a[c++]=e;else{g=e-257;k=G[g];0<I[g]&&(k+=A(this,I[g]));e=T(this,d);h=K[e];0<M[e]&&(h+=A(this,M[e]));c+k>f&&(a=this.e(),f=a.length);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c}; 12 w.prototype.e=function(){var b=new (u?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,c,f=this.c;if(u)b.set(f.subarray(32768,b.length));else{a=0;for(c=b.length;a<c;++a)b[a]=f[a+32768]}this.g.push(b);this.l+=b.length;if(u)f.set(f.subarray(d,d+32768));else for(a=0;32768>a;++a)f[a]=f[d+a];this.b=32768;return f}; 13 w.prototype.z=function(b){var d,a=this.input.length/this.a+1|0,c,f,e,g=this.input,h=this.c;b&&("number"===typeof b.p&&(a=b.p),"number"===typeof b.u&&(a+=b.u));2>a?(c=(g.length-this.a)/this.o[2],e=258*(c/2)|0,f=e<h.length?h.length+e:h.length<<1):f=h.length*a;u?(d=new Uint8Array(f),d.set(h)):d=h;return this.c=d}; 14 w.prototype.n=function(){var b=0,d=this.c,a=this.g,c,f=new (u?Uint8Array:Array)(this.l+(this.b-32768)),e,g,h,k;if(0===a.length)return u?this.c.subarray(32768,this.b):this.c.slice(32768,this.b);e=0;for(g=a.length;e<g;++e){c=a[e];h=0;for(k=c.length;h<k;++h)f[b++]=c[h]}e=32768;for(g=this.b;e<g;++e)f[b++]=d[e];this.g=[];return this.buffer=f}; 15 w.prototype.v=function(){var b,d=this.b;u?this.r?(b=new Uint8Array(d),b.set(this.c.subarray(0,d))):b=this.c.subarray(0,d):(this.c.length>d&&(this.c.length=d),b=this.c);return this.buffer=b};function U(b,d){var a,c;this.input=b;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=b[this.a++];c=b[this.a++];switch(a&15){case V:this.method=V;break;default:m(Error("unsupported compression method"))}0!==((a<<8)+c)%31&&m(Error("invalid fcheck flag:"+((a<<8)+c)%31));c&32&&m(Error("fdict flag is not supported"));this.q=new w(b,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} 16 U.prototype.k=function(){var b=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(b[this.a++]<<24|b[this.a++]<<16|b[this.a++]<<8|b[this.a++])>>>0;var c=d;if("string"===typeof c){var f=c.split(""),e,g;e=0;for(g=f.length;e<g;e++)f[e]=(f[e].charCodeAt(0)&255)>>>0;c=f}for(var h=1,k=0,l=c.length,q,p=0;0<l;){q=1024<l?1024:l;l-=q;do h+=c[p++],k+=h;while(--q);h%=65521;k%=65521}a!==(k<<16|h)>>>0&&m(Error("invalid adler-32 checksum"))}return d};var V=8;s("Zlib.Inflate",U);s("Zlib.Inflate.prototype.decompress",U.prototype.k);var W={ADAPTIVE:z.s,BLOCK:z.t},X,Y,Z,$;if(Object.keys)X=Object.keys(W);else for(Y in X=[],Z=0,W)X[Z++]=Y;Z=0;for($=X.length;Z<$;++Z)Y=X[Z],s("Zlib.Inflate.BufferType."+Y,W[Y]);}).call(this); //# sourceMappingURL=inflate.min.js.map 1 /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';var m=this;function q(c,d){var a=c.split("."),b=m;!(a[0]in b)&&b.execScript&&b.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&void 0!==d?b[e]=d:b=b[e]?b[e]:b[e]={}};var s="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function t(c){var d=c.length,a=0,b=Number.POSITIVE_INFINITY,e,f,g,h,k,l,p,n,r,K;for(n=0;n<d;++n)c[n]>a&&(a=c[n]),c[n]<b&&(b=c[n]);e=1<<a;f=new (s?Uint32Array:Array)(e);g=1;h=0;for(k=2;g<=a;){for(n=0;n<d;++n)if(c[n]===g){l=0;p=h;for(r=0;r<g;++r)l=l<<1|p&1,p>>=1;K=g<<16|n;for(r=l;r<e;r+=k)f[r]=K;++h}++g;h<<=1;k<<=1}return[f,a,b]};function u(c,d){this.g=[];this.h=32768;this.d=this.f=this.a=this.l=0;this.input=s?new Uint8Array(c):c;this.m=!1;this.i=v;this.s=!1;if(d||!(d={}))d.index&&(this.a=d.index),d.bufferSize&&(this.h=d.bufferSize),d.bufferType&&(this.i=d.bufferType),d.resize&&(this.s=d.resize);switch(this.i){case w:this.b=32768;this.c=new (s?Uint8Array:Array)(32768+this.h+258);break;case v:this.b=0;this.c=new (s?Uint8Array:Array)(this.h);this.e=this.A;this.n=this.w;this.j=this.z;break;default:throw Error("invalid inflate mode"); 2 }}var w=0,v=1,x={u:w,t:v}; 3 u.prototype.k=function(){for(;!this.m;){var c=y(this,3);c&1&&(this.m=!0);c>>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=void 0,h=void 0,k=b.length,l=void 0;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case w:for(;e+ 4 g>b.length;){l=k-e;g-=l;if(s)b.set(d.subarray(a,a+l),e),e+=l,a+=l;else for(;l--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case v:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(s)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(z,A);break;case 2:B(this);break;default:throw Error("unknown BTYPE: "+c);}}return this.n()}; 5 var C=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],D=s?new Uint16Array(C):C,E=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],F=s?new Uint16Array(E):E,G=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],H=s?new Uint8Array(G):G,I=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],J=s?new Uint16Array(I):I,L=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13, 6 13],M=s?new Uint8Array(L):L,N=new (s?Uint8Array:Array)(288),O,P;O=0;for(P=N.length;O<P;++O)N[O]=143>=O?8:255>=O?9:279>=O?7:8;var z=t(N),Q=new (s?Uint8Array:Array)(30),R,S;R=0;for(S=Q.length;R<S;++R)Q[R]=5;var A=t(Q);function y(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h;b<d;){if(f>=g)throw Error("input buffer is broken");a|=e[f++]<<b;b+=8}h=a&(1<<d)-1;c.f=a>>>d;c.d=b-d;c.a=f;return h} 7 function T(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],l,p;b<k&&!(f>=g);)a|=e[f++]<<b,b+=8;l=h[a&(1<<k)-1];p=l>>>16;c.f=a>>p;c.d=b-p;c.a=f;return l&65535} 8 function B(c){function d(a,c,b){var d,e=this.q,f,g;for(g=0;g<a;)switch(d=T(this,c),d){case 16:for(f=3+y(this,2);f--;)b[g++]=e;break;case 17:for(f=3+y(this,3);f--;)b[g++]=0;e=0;break;case 18:for(f=11+y(this,7);f--;)b[g++]=0;e=0;break;default:e=b[g++]=d}this.q=e;return b}var a=y(c,5)+257,b=y(c,5)+1,e=y(c,4)+4,f=new (s?Uint8Array:Array)(D.length),g,h,k,l;for(l=0;l<e;++l)f[D[l]]=y(c,3);if(!s){l=e;for(e=f.length;l<e;++l)f[D[l]]=0}g=t(f);h=new (s?Uint8Array:Array)(a);k=new (s?Uint8Array:Array)(b);c.q=0; 9 c.j(t(d.call(c,a,g,h)),t(d.call(c,b,g,k)))}u.prototype.j=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length-258,f,g,h,k;256!==(f=T(this,c));)if(256>f)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=F[g];0<H[g]&&(k+=y(this,H[g]));f=T(this,d);h=J[f];0<M[f]&&(h+=y(this,M[f]));b>=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; 10 u.prototype.z=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=T(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=F[g];0<H[g]&&(k+=y(this,H[g]));f=T(this,d);h=J[f];0<M[f]&&(h+=y(this,M[f]));b+k>e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; 11 u.prototype.e=function(){var c=new (s?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(s)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;a<b;++a)c[a]=e[a+32768]}this.g.push(c);this.l+=c.length;if(s)e.set(e.subarray(d,d+32768));else for(a=0;32768>a;++a)e[a]=e[d+a];this.b=32768;return e}; 12 u.prototype.A=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.v&&(a+=c.v));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=f<h.length?h.length+f:h.length<<1):e=h.length*a;s?(d=new Uint8Array(e),d.set(h)):d=h;return this.c=d}; 13 u.prototype.n=function(){var c=0,d=this.c,a=this.g,b,e=new (s?Uint8Array:Array)(this.l+(this.b-32768)),f,g,h,k;if(0===a.length)return s?this.c.subarray(32768,this.b):this.c.slice(32768,this.b);f=0;for(g=a.length;f<g;++f){b=a[f];h=0;for(k=b.length;h<k;++h)e[c++]=b[h]}f=32768;for(g=this.b;f<g;++f)e[c++]=d[f];this.g=[];return this.buffer=e}; 14 u.prototype.w=function(){var c,d=this.b;s?this.s?(c=new Uint8Array(d),c.set(this.c.subarray(0,d))):c=this.c.subarray(0,d):(this.c.length>d&&(this.c.length=d),c=this.c);return this.buffer=c};function U(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.B=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case V:this.method=V;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.r=new u(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} 15 U.prototype.k=function(){var c=this.input,d,a;d=this.r.k();this.a=this.r.a;if(this.B){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f<g;f++)e[f]=(e[f].charCodeAt(0)&255)>>>0;b=e}for(var h=1,k=0,l=b.length,p,n=0;0<l;){p=1024<l?1024:l;l-=p;do h+=b[n++],k+=h;while(--p);h%=65521;k%=65521}if(a!==(k<<16|h)>>>0)throw Error("invalid adler-32 checksum");}return d};var V=8;q("Zlib.Inflate",U);q("Zlib.Inflate.prototype.decompress",U.prototype.k);var W={ADAPTIVE:x.t,BLOCK:x.u},X,Y,Z,$;if(Object.keys)X=Object.keys(W);else for(Y in X=[],Z=0,W)X[Z++]=Y;Z=0;for($=X.length;Z<$;++Z)Y=X[Z],q("Zlib.Inflate.BufferType."+Y,W[Y]);}).call(this); //@ sourceMappingURL=inflate.min.js.map -
include/protocol.js
107 107 function process_buffer() { 108 108 "use strict"; 109 109 110 if (buf[0]!=ord("P")) 110 if (buf[0]!=ord("P")) { 111 111 throw "invalid packet header format: "+hex2(buf[0]); 112 } 112 113 113 114 var proto_flags = buf[1]; 114 115 if (proto_flags!=0) … … 169 170 var fn = ""; 170 171 try { 171 172 packet_type = packet[0]; 173 show("received a " + packet_type + " packet"); 172 174 fn = packet_handlers[packet_type]; 173 175 if (fn==undefined) 174 176 error("no packet handler for "+packet_type+"!"); -
include/window.js
7 7 * Based on shape.js 8 8 */ 9 9 10 var window_icons = {};11 //load the window icons12 var image_names = ["maximize", "minimize", "close"];13 function load_icon(name) {14 console.log("loading "+name);15 var tmp_canvas = document.createElement('canvas');16 var tmp_context = tmp_canvas.getContext('2d');17 var image = new Image();18 image.onload = function() {19 show(""+name+"="+image);20 tmp_context.drawImage(image, 0, 0);21 var image_data = tmp_context.getImageData(0, 0, image.width, image.height);22 window_icons[name] = image_data;23 };24 image.src = '/include/'+name+'.png';25 }26 27 for (var i in image_names) {28 load_icon(image_names[i]);29 }30 31 10 /** 32 * A simple button we use to decorate the window.33 */34 function Button(canvas_state, name, x, y, w, h, fill, icon_name, click_callback) {35 "use strict";36 this.state = canvas_state;37 this.name = name;38 this.x = x || 0;39 this.y = y || 0;40 this.w = w || 1;41 this.h = h || 1;42 var ctx = canvas_state.canvas.getContext('2d');43 this.image = ctx.createImageData(w, h);44 //var data = this.image.data;45 for (var i=0; i<w*h*4; i++) {46 this.image.data[i] = (fill || 0xAA) &0xFF;47 }48 var icon = window_icons[icon_name];49 if (icon) {50 this.update_image(icon.width, icon.height, "premult_argb32", icon.data);51 }52 this.click_callback = click_callback;53 }54 Button.prototype.draw_at = function(ctx, x, y) {55 "use strict";56 //draw the window pixels:57 ctx.putImageData(this.image, this.x + x, this.y + y);58 };59 function rectangle_contains(rect, mx, my) {60 "use strict";61 // All we have to do is make sure the Mouse X,Y fall in the area between62 // the shape's X and (X + Height) and its Y and (Y + Height)63 return (rect.x <= mx) && (rect.x + rect.w >= mx) &&64 (rect.y <= my) && (rect.y + rect.h >= my);65 };66 Button.prototype.get_geometry = function() {67 "use strict";68 return { x : this.x, y : this.y, w : this.w, h : this.h };69 };70 Button.prototype.contains = function(mx, my) {71 "use strict";72 return rectangle_contains(this.get_geometry(), mx, my);73 };74 Button.prototype.update_image = function(w, h, pixel_format, data) {75 "use strict";76 if (pixel_format!="premult_argb32") {77 return;78 }79 var s, d, i;80 //to make this faster and better looking,81 //we could draw to a temporary canvas to scale it,82 //as per: http://stackoverflow.com/a/3449416/42875183 //here we just scale it by hand84 for (var x=0; x<this.w; x++) {85 for (var y=0; y<this.h; y++) {86 //destination index (simple)87 d = ((y*this.w) + x) * 4;88 //source index (scaled)89 s = (Math.round(y*h/this.h)*w + Math.round(w*x/this.w)) * 4;90 for (i=0; i<4; i++) {91 this.image.data[d+i] = data[s+i];92 }93 }94 }95 }96 97 /**98 * toString allows us to identify buttons:99 */100 Button.prototype.toString = function() {101 "use strict";102 return "Button("+this.name+")";103 };104 105 106 107 /**108 11 * This is the class representing a window we draw on the canvas. 109 12 * It has a geometry, it may have borders and a top bar. 110 13 * The contents of the window is an image, which gets updated 111 14 * when we receive pixels from the server. 112 15 */ 113 function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, 114 geometry_cb, mouse_move_cb, mouse_click_cb, window_closed) { 16 function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, geometry_cb, mouse_move_cb, mouse_click_cb, set_focus_cb, window_closed_cb, htmldiv) { 115 17 "use strict"; 18 // use me in jquery callbacks as we lose 'this' 19 var me = this; 116 20 //keep reference to the canvas: 117 this.state = canvas_state; 21 this.canvas = canvas_state; 22 this.div = jQuery("#" + String(wid)); 23 118 24 //callbacks start null until we finish init: 119 25 this.geometry_cb = null; 120 26 this.mouse_move_cb = null; … … 121 27 this.mouse_click_cb = null; 122 28 this.window_closed_cb = null; 123 29 124 //styling:125 this.borderColor = '#101028';126 this.topBarColor = '#B8B8C0';127 128 30 //the window "backing": 129 31 this.image = null; 130 32 … … 136 38 137 39 //window attributes: 138 40 this.title = null; 41 this.windowtype = null; 139 42 this.fullscreen = false; 140 43 this.saved_geometry = null; 141 44 this.maximized = false; 142 45 this.focused = false; 143 46 144 // not the real geometry we will use,145 // but enough to avoid errors if update_metadata fires changes47 //these values represent the internal geometry 48 //i.e. geometry as windows appear to the compositor 146 49 this.x = x; 147 50 this.y = y; 148 51 this.w = w; 149 52 this.h = h; 150 53 151 this.buttons = {}; 54 // get offsets 55 this.leftoffset = parseInt(jQuery(this.div).css('border-left-width'), 10); 56 this.rightoffset = parseInt(jQuery(this.div).css('border-right-width'), 10); 57 this.topoffset = parseInt(jQuery(this.div).css('border-top-width'), 10); 58 this.bottomoffset = parseInt(jQuery(this.div).css('border-bottom-width'), 10); 152 59 60 // update metadata 153 61 this.update_metadata(metadata); 154 62 155 // the space taken by window decorations: 156 this.calculate_offsets(); 157 158 if (!this.fullscreen && !this.maximized) { 159 // if fullscreen or maximized, the metadata update will have set the new size already 160 // account for borders, and try to make the image area map to (x,y): 161 var rx = (x || 0) - this.borderWidth; 162 var ry = (y || 0) - this.topBarHeight + this.borderWidth; 163 var rw = (w || 1) + this.borderWidth*2; 164 var rh = (h || 1) + this.borderWidth*2 + this.topBarHeight; 165 this.move_resize(rx, ry, rw, rh); 166 //show("after move resize: ("+this.w+", "+this.h+")"); 63 // Hook up the events we want to receive: 64 this.event_listeners = [] 65 var listeners = [ 66 ['mousedown' , true], 67 ['mouseup' , true], 68 ['mousemove' , true] 69 ]; 70 for (i = 0; i < listeners.length; i += 1) { 71 var l = listeners[i]; 72 this.registerEventListener(l[0], l[1]); 167 73 } 168 74 169 75 // now safe to assign the callbacks: … … 170 76 this.geometry_cb = geometry_cb || null; 171 77 this.mouse_move_cb = mouse_move_cb || null; 172 78 this.mouse_click_cb = mouse_click_cb || null; 173 this.window_closed_cb = window_closed || null;79 this.window_closed_cb = window_closed_cb || null; 174 80 175 //create the buttons: 176 if (!this.override_redirect) { 177 this.create_buttons(); 81 // create the decoration as part of the window, style is in CSS 82 jQuery(this.div).addClass("window"); 83 jQuery(this.div).addClass("window-" + this.windowtype); 84 // add a title bar to this window if we need to 85 if((this.windowtype == "NORMAL") || (this.windowtype == "DIALOG") || (this.windowtype == "UTILITY")) { 86 if(!this.override_redirect) { 87 // create header 88 jQuery(this.div).prepend('<div id="head' + String(wid) + '" class="windowhead"> <span class="windowtitle" id="title' + String(wid) + '">' + this.title + '</span> <span class="windowbuttons"> <span id="close' + String(wid) + '"><img src="include/close.png" /></span> </span></div>'); 89 // make draggable 90 jQuery(this.div).draggable({ 91 cancel: "canvas", 92 stop: function(e, ui) { 93 me.handle_moved(ui); 94 } 95 }); 96 // attach resize handles 97 jQuery(this.div).resizable({ 98 helper: "ui-resizable-helper", 99 stop: function(e, ui) { 100 me.handle_resized(ui); 101 } 102 }); 103 this.d_header = '#head' + String(wid); 104 this.d_closebtn = '#close' + String(wid); 105 // adjust top offset 106 this.topoffset = this.topoffset + parseInt(jQuery(this.d_header).css('height'), 10); 107 // assign some interesting callbacks 108 jQuery('#head' + String(wid)).click(function() { 109 set_focus_cb(wid); 110 }); 111 jQuery('#close' + String(wid)).click(function() { 112 window_closed_cb(wid); 113 }); 114 } 178 115 } 116 117 // need to update the CSS geometry 118 this.ensure_visible(); 119 this.updateCSSGeometry(); 120 //show("placing new window at "+this.x+","+this.y); 121 179 122 //create the image holding the pixels (the "backing"): 180 123 this.create_image_backing(); 181 canvas_state.addShape(this);182 124 }; 183 125 184 /** 185 * Creates the minimize, maximize and close buttons. 186 */ 187 XpraWindow.prototype.create_buttons = function() { 188 "use strict"; 189 var w = 24; 190 var h = 24; 191 var self = this; 192 this.buttons["icon"] = new Button(this.state, "icon", this.borderWidth, this.borderWidth, w, h, 0x11, null, null); 193 /*this.buttons["minimize"] = new Button(this.state, "minimize", this.w-(w+2)*3, this.borderWidth, w, h, 0x44, function() { 194 //TODO! 195 });*/ 196 this.buttons["maximize"] = new Button(this.state, "maximize", this.w-(w+2)*2, this.borderWidth, w, h, 0x66, "maximize", function() { 197 var m = !self.maximized; 198 self.client_properties["maximized"] = m; 199 self.set_maximized(m); 200 }); 201 this.buttons["close"] = new Button(this.state, "close", this.w-(w+2)*1, this.borderWidth, w, h, 0x99, "close", function() { 202 if (self.window_closed_cb) { 203 self.window_closed_cb(self); 204 } 205 }); 206 }; 207 /** 208 * Ensures that the buttons are always in the same place 209 * after a resize. 210 */ 211 XpraWindow.prototype.move_buttons = function() { 212 var w = 24; 213 if ("maximize" in this.buttons) { 214 this.buttons["maximize"].x = this.w-(w+2)*2; 126 XpraWindow.prototype.ensure_visible = function() { 127 var oldx = this.x; 128 var oldy = this.y; 129 // for now make sure we don't out of top left 130 // this will be much smarter! 131 if(oldx <= 0) { 132 this.x = 0 + this.leftoffset; 215 133 } 216 if ("close" in this.buttons) {217 this. buttons["close"].x = this.w-(w+2)*1;134 if(oldy <= 10) { 135 this.y = 0 + this.topoffset; 218 136 } 137 if((oldx != this.x) || (oldy != this.y)) { 138 this.updateCSSGeometry(); 139 return false; 140 } 141 return true; 219 142 } 220 143 144 XpraWindow.prototype.updateCSSGeometry = function() { 145 // set size of canvas if needed 146 if(this.canvas.width != this.w) { 147 this.canvas.width = this.w; 148 } 149 if(this.canvas.height != this.h) { 150 this.canvas.height = this.h; 151 } 152 // work out outer size 153 this.outerH = this.h + this.topoffset + this.bottomoffset; 154 this.outerW = this.w + this.leftoffset + this.rightoffset; 155 // set width and height 156 jQuery(this.div).css('width', this.outerW); 157 jQuery(this.div).css('height', this.outerH); 158 // set CSS attributes to outerX and outerY 159 this.outerX = this.x - this.leftoffset; 160 this.outerY = this.y - this.topoffset; 161 jQuery(this.div).css('left', this.outerX); 162 jQuery(this.div).css('top', this.outerY); 163 } 221 164 165 XpraWindow.prototype.updateFocus = function() { 166 if(this.focused) { 167 // set focused style to div 168 jQuery(this.div).addClass("windowinfocus"); 169 170 } else { 171 // set not in focus style 172 jQuery(this.div).removeClass("windowinfocus"); 173 } 174 } 175 176 XpraWindow.prototype.registerEventListener = function(event_type, useCapture) { 177 var self = this; 178 var fn_name = "on_"+event_type; 179 var handler = self[fn_name]; 180 var fn = function(e) { 181 handler.call(self, e); 182 }; 183 this.event_listeners[event_type] = fn; 184 this.canvas["on"+event_type] = fn; 185 }; 186 187 XpraWindow.prototype.getMouse = function(e) { 188 "use strict"; 189 190 // get mouse position take into account scroll 191 var mx = e.clientX + jQuery(document).scrollLeft(); 192 var my = e.clientY + jQuery(document).scrollTop(); 193 194 var mbutton = 0; 195 if ("which" in e) // Gecko (Firefox), WebKit (Safari/Chrome) & Opera 196 mbutton = Math.max(0, e.which); 197 else if ("button" in e) // IE, Opera (zero based) 198 mbutton = Math.max(0, e.button)+1; 199 //show("getmouse: button="+mbutton+", which="+e.which+", button="+e.button); 200 201 // We return a simple javascript object (a hash) with x and y defined 202 return {x: mx, y: my, button: mbutton}; 203 }; 204 205 XpraWindow.prototype.on_mousemove = function(e) { 206 var mouse = this.getMouse(e), 207 mx = mouse.x, 208 my = mouse.y; 209 210 var modifiers = []; 211 var buttons = []; 212 this.handle_mouse_move(mx, my, modifiers, buttons); 213 214 }; 215 216 XpraWindow.prototype.on_mousedown = function(e) { 217 var mouse, mx, my, shapes, l, i, mySel; 218 219 mouse = this.getMouse(e); 220 mx = mouse.x; 221 my = mouse.y; 222 223 // pass the click to the area: 224 var modifiers = []; 225 var buttons = []; 226 this.handle_mouse_click(mouse.button, true, mx, my, modifiers, buttons); 227 return; 228 }; 229 230 XpraWindow.prototype.on_mouseup = function(e) { 231 // if not handling it ourselves, pass it down: 232 var mouse = this.getMouse(e), 233 mx = mouse.x, 234 my = mouse.y; 235 if (!this.dragging) { 236 var modifiers = []; 237 var buttons = []; 238 this.handle_mouse_click(mouse.button, false, mx, my, modifiers, buttons); 239 } 240 241 this.dragging = false; 242 }; 243 222 244 /** 223 245 * toString allows us to identify windows by their unique window id. 224 246 */ … … 235 257 var previous_image = this.image; 236 258 var img_geom = this.get_internal_geometry(); 237 259 //show("createImageData: "+img_geom.toSource()); 238 this.image = canvas_state.canvas.getContext('2d').createImageData(img_geom.w, img_geom.h);260 this.image = this.canvas.getContext('2d').createImageData(img_geom.w, img_geom.h); 239 261 if (previous_image) { 240 262 //copy previous pixels to new image, ignoring bit gravity 241 //TODO!263 this.canvas.getContext('2d').putImageData(previous_image, 0, 0); 242 264 } 243 265 }; 244 266 245 267 /** 246 * Depending on the type of window (OR, fullscreen)247 * we calculate the offsets from the edge of the window248 * to the contents of the window.249 */250 XpraWindow.prototype.calculate_offsets = function() {251 "use strict";252 if (this.override_redirect || this.fullscreen) {253 //no borders or top bar at all:254 this.borderWidth = 0;255 this.topBarHeight = 0;256 }257 else {258 //regular borders and top bar:259 this.borderWidth = 2;260 this.topBarHeight = 24;261 }262 this.offsets = [this.borderWidth+this.topBarHeight, this.borderWidth, this.borderWidth, this.borderWidth];263 };264 265 /**266 268 * Update our metadata cache with new key-values, 267 269 * then call set_metadata with these new key-values. 268 270 */ … … 288 290 } 289 291 if ("title" in metadata) { 290 292 this.title = metadata["title"]; 291 //redraw everything (a bit wasteful): 292 this.state.invalidate(); 293 jQuery('#title' + this.wid).html(this.title); 293 294 } 295 if ("window-type" in metadata) { 296 this.windowtype = metadata["window-type"][0]; 297 } 294 298 }; 295 299 296 300 /** … … 299 303 */ 300 304 XpraWindow.prototype.save_geometry = function() { 301 305 "use strict"; 306 /* 307 308 TODO 309 310 * probably won't need to do this just use CSS to display: none; 311 * see restore_geometry 312 302 313 if (this.x==undefined || this.y==undefined) 303 314 return; 304 315 this.saved_geometry = { … … 308 319 "h" : this.h, 309 320 "maximized" : this.maximized, 310 321 "fullscreen" : this.fullscreen}; 322 */ 311 323 } 312 324 /** 313 325 * Restores the saved geometry (if it exists). … … 314 326 */ 315 327 XpraWindow.prototype.restore_geometry = function() { 316 328 "use strict"; 329 /* 330 331 TODO 332 333 * we probably won't need to do this since we can just change CSS to 334 * display: none; to hide it! 335 317 336 if (this.saved_geometry==null) { 318 337 return; 319 338 } … … 323 342 this.h = this.saved_geometry["h"]; 324 343 this.maximized = this.saved_geometry["maximized"]; 325 344 this.fullscreen = this.saved_geometry["fullscreen"]; 345 */ 326 346 }; 327 347 328 348 /** … … 330 350 */ 331 351 XpraWindow.prototype.set_maximized = function(maximized) { 332 352 "use strict"; 353 /* 354 355 TODO 356 333 357 //show("set_maximized("+maximized+")"); 334 358 if (this.maximized==maximized) { 335 359 return; … … 338 362 this.maximized = maximized; 339 363 this.calculate_offsets(); 340 364 this.handle_resize(); 365 */ 341 366 }; 342 367 /** 343 368 * Fullscreen / unfullscreen the window. … … 344 369 */ 345 370 XpraWindow.prototype.set_fullscreen = function(fullscreen) { 346 371 "use strict"; 372 /* 373 374 TODO 375 347 376 //show("set_fullscreen("+fullscreen+")"); 348 377 if (this.fullscreen==fullscreen) { 349 378 return; … … 352 381 this.fullscreen = fullscreen; 353 382 this.calculate_offsets(); 354 383 this.handle_resize(); 384 */ 355 385 }; 356 386 357 387 /** … … 361 391 */ 362 392 XpraWindow.prototype.max_save_restore = function(use_all_space) { 363 393 "use strict"; 394 /* 395 396 TODO 397 398 * again see save|restore_geometry 399 364 400 if (use_all_space) { 365 401 this.save_geometry(); 366 402 this.fill_canvas(); … … 368 404 else { 369 405 this.restore_geometry(); 370 406 } 407 */ 371 408 }; 372 409 373 410 /** … … 375 412 */ 376 413 XpraWindow.prototype.fill_canvas = function() { 377 414 "use strict"; 415 /* 416 417 TODO 418 378 419 this.x = 0; 379 420 this.y = 0; 380 this.w = this.state.width; 381 this.h = this.state.height; 421 this.w = 640; 422 this.h = 480; 423 */ 382 424 }; 383 425 384 426 /** 385 427 * We have resized the window, so we need to: 428 * - work out new position of internal canvas 429 * - update external CSS position 386 430 * - resize the backing image 387 * - tell the canvas to repaint us388 431 * - fire the geometry_cb 389 432 */ 390 XpraWindow.prototype.handle_resize = function() {433 XpraWindow.prototype.handle_resized = function(e) { 391 434 "use strict"; 435 // this function is called on local resize only, 436 // remote resize will call this.resize() 437 // need to update the internal geometry 438 this.w = e.size.width - this.leftoffset - this.rightoffset; 439 this.h = e.size.height - this.topoffset - this.bottomoffset; 440 // then update CSS and redraw backing 441 this.updateCSSGeometry(); 392 442 this.create_image_backing(); 393 this.state.invalidate(); 394 this.move_buttons(); 395 if (this.geometry_cb!=null) { 396 this.geometry_cb(this); 397 } 443 // send geometry callback 444 this.geometry_cb(this); 398 445 }; 399 446 400 447 /** 448 * Like handle_resized, except we should 449 * store internal geometry, external is always in CSS left and top 450 */ 451 XpraWindow.prototype.handle_moved = function(e) { 452 "use strict"; 453 // add on padding to the event position so that 454 // it reflects the internal geometry of the canvas 455 this.x = e.position.left + this.leftoffset; 456 this.y = e.position.top + this.topoffset; 457 // make sure we are visible after move 458 this.ensure_visible(); 459 // tell remote we have moved window 460 this.geometry_cb(this); 461 } 462 463 /** 401 464 * The canvas ("screen") has been resized, we may need to resize our window to match 402 465 * if it is fullscreen or maximized. 403 466 */ 404 467 XpraWindow.prototype.canvas_resized = function() { 405 468 "use strict"; 469 /* 470 471 TODO 472 406 473 if (this.fullscreen || this.maximized) { 407 474 this.fill_canvas(); 408 475 this.handle_resize(); 409 476 } 477 */ 410 478 }; 411 479 412 XpraWindow.prototype.move_resize = Shape.prototype.move_resize; 413 XpraWindow.prototype.move = Shape.prototype.move; 414 XpraWindow.prototype.resize = Shape.prototype.resize; 415 XpraWindow.prototype.get_window_geometry = Shape.prototype.get_window_geometry; 480 /** 481 * Things ported from original shape 482 */ 416 483 484 XpraWindow.prototype.move_resize = function(x, y, w, h) { 485 "use strict"; 486 // only do it if actually changed! 487 if(!(this.w == w) || !(this.h == h) || !(this.x == x) || !(this.y == y)) { 488 this.w = w; 489 this.h = h; 490 this.x = x; 491 this.y = y; 492 if(!this.ensure_visible()) { 493 // we had to move the window so that it was visible 494 // is this the right thing to do? 495 this.geometry_cb(this); 496 } 497 this.updateCSSGeometry(); 498 this.create_image_backing(); 499 } 500 }; 501 502 XpraWindow.prototype.move = function(x, y) { 503 "use strict"; 504 this.move_resize(x, y, this.w, this.h); 505 }; 506 507 XpraWindow.prototype.resize = function(w, h) { 508 "use strict"; 509 this.move_resize(this.x, this.y, w, h); 510 }; 511 417 512 /** 418 513 * Returns the geometry of the window backing image, 419 514 * the inner window geometry (without any borders or top bar). 420 515 */ 421 XpraWindow.prototype.get_internal_geometry = function( ctx) {516 XpraWindow.prototype.get_internal_geometry = function() { 422 517 "use strict"; 423 // This should always be true: 424 //this.image.width = this.w - this.borderWidth*2; 425 //this.image.height = this.h - (this.borderWidth*2 + this.topBarHeight); 426 return { x : this.x+this.borderWidth, 427 y : this.y+this.borderWidth+this.topBarHeight, 428 w : this.w - this.borderWidth*2, 429 h : this.h - (this.borderWidth*2 + this.topBarHeight)}; 518 /* we store the internal geometry only 519 * and work out external geometry on the fly whilst 520 * updating CSS 521 */ 522 return { x : this.x, 523 y : this.y, 524 w : this.w, 525 h : this.h}; 430 526 }; 431 527 432 528 /** 433 * If the click is in the "internal_geometry" (see above),529 * Handle mouse click from this window's canvas, 434 530 * then we fire "mouse_click_cb" (if it is set). 435 531 */ 436 532 XpraWindow.prototype.handle_mouse_click = function(button, pressed, mx, my, modifiers, buttons) { 437 533 "use strict"; 438 var igeom = this.get_internal_geometry(); 439 if (this.mouse_click_cb!=null && rectangle_contains(igeom, mx, my)) { 440 this.mouse_click_cb(this, button, pressed, mx, my, modifiers, buttons); 441 return; 442 } 443 //maybe one of the buttons: 444 //(use relative coordinates) 445 var x = mx-this.x; 446 var y = my-this.y 447 for (var name in this.buttons) { 448 var button = this.buttons[name]; 449 if (button.contains(x, y)) { 450 show("clicked on button "+name); 451 var cb = button.click_callback; 452 if (cb) { 453 cb(); 454 } 455 return; 456 } 457 } 534 console.log("got mouse click at ", mx, my) 535 // mouse click event is from canvas just for this window so no need to check 536 // internal geometry anymore 537 this.mouse_click_cb(this, button, pressed, mx, my, modifiers, buttons); 458 538 }; 459 539 460 540 /** 461 * If the click is in the "internal_geometry" (see above),541 * Handle mouse move from this window's canvas, 462 542 * then we fire "mouse_move_cb" (if it is set). 463 543 */ 464 544 XpraWindow.prototype.handle_mouse_move = function(mx, my, modifiers, buttons) { 465 545 "use strict"; 466 var igeom = this.get_internal_geometry(); 467 if (this.mouse_move_cb!=null && rectangle_contains(igeom, mx, my)) { 468 this.mouse_move_cb(this, mx, my, modifiers, buttons); 469 } 546 this.mouse_move_cb(this, mx, my, modifiers, buttons); 470 547 }; 471 548 472 549 473 550 XpraWindow.prototype.update_icon = function(w, h, pixel_format, data) { 474 551 "use strict"; 475 var icon = this.buttons["icon"]; 476 if (icon) { 477 icon.update_image(w, h, pixel_format, data); 478 this.state.invalidate(); 479 } 552 // update icon 553 // TODO 480 554 } 481 555 482 556 /** … … 487 561 */ 488 562 XpraWindow.prototype.draw = function(ctx) { 489 563 "use strict"; 490 491 if (!this.override_redirect && !this.fullscreen) {492 //draw window frame:493 this.draw_frame(ctx);494 }495 496 564 //draw the window pixels: 497 ctx.putImageData(this.image, this.x + this.borderWidth, this.y + this.borderWidth + this.topBarHeight); 498 499 if (this.state.selection === this && !this.override_redirect) { 500 //window.alert("Shape.prototype.draw_selection="+Shape.prototype.draw_selection); 501 this.draw_selection(ctx); 502 } 565 var ctx = this.canvas.getContext('2d'); 566 ctx.putImageData(this.image, 0, 0); 503 567 }; 504 568 505 /**506 * Draws the window frame:507 * a simple rectangle around the edge.508 */509 XpraWindow.prototype.draw_frame = function(ctx) {510 "use strict";511 569 512 // draw border:513 ctx.strokeStyle = this.borderColor;514 ctx.lineWidth = this.borderWidth;515 var hw = this.borderWidth/2;516 ctx.strokeRect(this.x+hw,this.y+hw,this.w-this.borderWidth,this.h-this.borderWidth);517 518 // draw top bar:519 ctx.fillStyle = this.topBarColor;520 ctx.fillRect(this.x+this.borderWidth, this.y+this.borderWidth, this.w-this.borderWidth*2, this.topBarHeight);521 // draw title:522 if (this.title) {523 var size = 18;524 ctx.font = ""+size+"px sans-serif";525 ctx.fillStyle = "#FFFFFF";526 ctx.fillText(this.title, this.x+32, this.y+this.borderWidth+this.topBarHeight-size/3);527 }528 529 // draw buttons:530 for (var name in this.buttons) {531 var button = this.buttons[name];532 button.draw_at(ctx, this.x, this.y);533 }534 };535 536 537 570 /** 538 * Draws border and selection rectangles539 * which indicate that the window is selected540 */541 XpraWindow.prototype.draw_selection = function(ctx) {542 "use strict";543 if (this.maximized || this.fullscreen) {544 return;545 }546 var i, cur, half;547 548 ctx.strokeStyle = this.state.selectionColor;549 ctx.lineWidth = this.state.selectionWidth;550 ctx.strokeRect(this.x,this.y,this.w,this.h);551 552 // draw the boxes553 half = this.state.selectionBoxSize / 2;554 555 // 0 1 2556 // 3 4557 // 5 6 7558 559 // top left, middle, right560 this.state.selectionHandles[0].x = this.x-half;561 this.state.selectionHandles[0].y = this.y-half;562 563 this.state.selectionHandles[1].x = this.x+this.w/2-half;564 this.state.selectionHandles[1].y = this.y-half;565 566 this.state.selectionHandles[2].x = this.x+this.w-half;567 this.state.selectionHandles[2].y = this.y-half;568 569 //middle left570 this.state.selectionHandles[3].x = this.x-half;571 this.state.selectionHandles[3].y = this.y+this.h/2-half;572 573 //middle right574 this.state.selectionHandles[4].x = this.x+this.w-half;575 this.state.selectionHandles[4].y = this.y+this.h/2-half;576 577 //bottom left, middle, right578 this.state.selectionHandles[6].x = this.x+this.w/2-half;579 this.state.selectionHandles[6].y = this.y+this.h-half;580 581 this.state.selectionHandles[5].x = this.x-half;582 this.state.selectionHandles[5].y = this.y+this.h-half;583 584 this.state.selectionHandles[7].x = this.x+this.w-half;585 this.state.selectionHandles[7].y = this.y+this.h-half;586 587 ctx.fillStyle = this.state.selectionBoxColor;588 for (i = 0; i < 8; i += 1) {589 cur = this.state.selectionHandles[i];590 ctx.fillRect(cur.x, cur.y, this.state.selectionBoxSize, this.state.selectionBoxSize);591 }592 };593 594 /**595 571 * Updates the window image with new pixel data 596 572 * we have received from the server. 597 573 */ … … 625 601 var stride = this.image.width*4; 626 602 //and we can paint the canvas with it 627 603 //(if we have transparency, we should probably repaint what is underneath...) 628 var ctx = this. state.ctx;604 var ctx = this.canvas.getContext('2d'); 629 605 630 606 if (x==0 && width==this.image.width && y+height<=this.image.height) { 631 607 //take a shortcut: copy all lines … … 633 609 634 610 if (this.focused) { 635 611 //shortcut: paint canvas directly 636 ctx.putImageData(this.image, this.x + this.borderWidth, this.y + this.borderWidth + this.topBarHeight);612 ctx.putImageData(this.image, 0, 0); 637 613 return; 614 } else { 615 // window is not in focus but should we draw it anyway? 616 ctx.putImageData(this.image, 0, 0); 617 return; 638 618 } 639 619 } 640 620 else if (x+width<=this.image.width && y+height<=this.image.height) { … … 650 630 651 631 if (this.focused) { 652 632 //shortcut: paint canvas directly 653 ctx.putImageData(img, this.x + this.borderWidth + x, this.y + this.borderWidth + this.topBarHeight +y);633 ctx.putImageData(img, x, y); 654 634 return; 635 } else { 636 // window is not in focus but should we draw it anyway? 637 ctx.putImageData(img, x, y); 638 return; 655 639 } 656 640 } 657 641 else { … … 658 642 //no action taken, no need to invalidate 659 643 return; 660 644 } 661 this.state.invalidate();645 //this.state.invalidate(); 662 646 }; 663 647 664 648 /** … … 666 650 */ 667 651 XpraWindow.prototype.destroy = function destroy() { 668 652 "use strict"; 669 if (this.state!=null) { 670 this.state.removeShape(this); 671 this.state = null; 672 } 653 // remove div 654 this.div.remove() 673 655 }; 674 675 /**676 * Determine if a point is inside the window's contents677 */678 XpraWindow.prototype.contains = function(mx, my) {679 "use strict";680 // All we have to do is make sure the Mouse X,Y fall in the area between681 // the shape's X and (X + Height) and its Y and (Y + Height)682 return (this.x <= mx) && (this.x + this.w >= mx) &&683 (this.y <= my) && (this.y + this.h >= my);684 };685 686 /**687 * Determine if a point is inside the window's grab area.688 * (the edges that are not part of the image backing)689 */690 XpraWindow.prototype.is_grab_area = function(mx, my) {691 "use strict";692 if (!this.contains(mx, my)) {693 return false;694 }695 // use window relative values from here on:696 var x = mx - this.x;697 var y = my - this.y;698 699 // must be in the border area:700 if (!(y<=this.offsets[0] || my>=(this.h-this.offsets[2]) ||701 x<=this.offsets[3] || x>=(this.w-this.offsets[1]))) {702 return false;703 }704 705 // check that this isn't one of the buttons:706 for (var name in this.buttons) {707 var button = this.buttons[name];708 if (button.contains(x, y)) {709 return false;710 }711 }712 return true;713 }; -
index.html
19 19 width: 100%; 20 20 padding: 0; 21 21 margin: 0; 22 color: white;22 overflow: hidden; 23 23 } 24 div.form {25 background-color: #708090;26 border: 4px solid #506070;27 box-sizing: border-box;28 }29 24 div#disconnect_form { 30 25 display: none; 31 26 } 32 fieldset {33 margin: 0px;34 border: 0px solid black;35 }36 27 canvas { 37 border: 0px solid black; 38 display: none; 28 border: none; 29 padding: 0; 30 margin: 0; 39 31 } 32 .window { 33 border: 1px; 34 position: absolute; 35 box-shadow: 0px 10px 25px rgba(0, 0, 0, 0.5); 36 padding: 0; 37 overflow: hidden; /* required for radius clip */ 38 } 39 .window-NORMAL { 40 border-radius: 6px; 41 } 42 .window-DROPDOWN_MENU { 43 z-index: 20000; 44 } 45 .window-TOOLTIP { 46 z-index: 20000; 47 } 48 .window-POPUP_MENU { 49 z-index: 20000; 50 } 51 .window-COMBO { 52 z-index: 20000; 53 } 54 .window-UTILITY { 55 z-index: 15000; 56 } 57 .windowhead { 58 height: 30px; 59 color: #000000; 60 background-color: #c0c0c0; 61 } 62 .windowtitle { 63 display: inline-block; 64 float: left; 65 width: 80%; 66 overflow: hidden; 67 text-overflow:ellipsis; 68 white-space: nowrap; 69 padding: 5px; 70 cursor: default; 71 } 72 .windowbuttons { 73 display: inline-block; 74 float: right; 75 padding-right: 5px; 76 cursor: pointer; 77 padding-top: 7px; 78 } 79 .windowinfocus { 80 z-index: 10000; 81 } 82 .ui-resizable-helper { 83 border: 2px dotted #00F; 84 z-index: 30000 !important; 85 } 86 .ui-resizable-handle { position: absolute;font-size: 0.1px; display: block; } 87 .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } 88 .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } 89 .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } 90 .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } 91 .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } 92 .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } 93 .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } 94 .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } 95 .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;} 40 96 </style> 41 97 98 <script type="text/javascript" src="include/util.js"></script> 42 99 <script type="text/javascript" src="include/websock.js"></script> 43 100 <script type="text/javascript" src="include/bencode.js"></script> 44 101 <script type="text/javascript" src="include/inflate.min.js"></script> 45 102 <script type="text/javascript" src="include/protocol.js"></script> 46 103 <script type="text/javascript" src="include/keycodes.js"></script> 47 <script type="text/javascript" src="include/shapes.js"></script>48 104 <script type="text/javascript" src="include/window.js"></script> 49 <script type="text/javascript" src="include/util.js"></script> 105 <script type="text/javascript" src="include/jquery.min.js"></script> 106 <script type="text/javascript" src="include/jqueryui.min.js"></script> 50 107 51 108 <script> 52 109 var protocol; 53 110 var status = "ready"; 54 111 112 var topwindow = null; 113 var topindex = 0; 114 55 115 var id_to_window = {}; 56 116 var window_to_id = {}; 57 117 var focus = -1; 58 118 var screen_size_change_pending = false; 59 119 120 // from http://stackoverflow.com/a/11744120 121 var w = window, 122 d = document, 123 e = d.documentElement, 124 g = d.getElementsByTagName('body')[0]; 125 126 var screen_width = w.innerWidth || e.clientWidth || g.clientWidth; 127 var screen_height = w.innerHeight|| e.clientHeight|| g.clientHeight; 128 60 129 var OLD_ENCODING_NAMES_TO_NEW = {"x264" : "h264", "vpx" : "vp8"}; 61 130 var RGB_FORMATS = ["RGBX", "RGBA"]; 62 var canvas_state = null;63 131 64 132 var caps_lock = null; 65 133 var alt_modifier = null; 66 134 var meta_modifier = null; 67 135 136 // disable right click menu: 137 window.oncontextmenu = function(e) { 138 //showCustomMenu(); 139 return false; 140 } 141 68 142 /** 69 143 * Returns the modifiers set for the current event. 70 144 * We get the list of modifiers using "get_event_modifiers" … … 95 169 */ 96 170 function processKeyEvent(pressed, event) { 97 171 "use strict"; 98 if (canvas_state==null)99 return;100 172 // MSIE hack 101 173 if (window.event) 102 174 event = window.event; … … 125 197 if ((caps_lock && shift) || (!caps_lock && !shift)) 126 198 str = str.toLowerCase(); 127 199 128 var win = canvas_state.topOfStack(); 129 if (win != null) { 200 if (topwindow != null) { 130 201 //show("win="+win.toSource()+", keycode="+keycode+", modifiers=["+modifiers+"], str="+str); 131 var wid = window_to_id[win]; 132 var packet = ["key-action", wid, keyname, pressed, modifiers, keyval, str, keycode, group]; 202 var packet = ["key-action", topwindow, keyname, pressed, modifiers, keyval, str, keycode, group]; 133 203 send(packet); 134 204 } 135 205 } … … 256 326 //These are callbacks from XpraWindow to us 257 327 //to notify us of window clicks and events: 258 328 259 function window_closed(wi n) {329 function window_closed(wid) { 260 330 "use strict"; 261 var wid = window_to_id[win];262 331 send(["close-window", wid]); 263 332 } 264 333 function window_geometry_changed(win) { 265 334 "use strict"; 266 //show("window_geometry_changed("+win+") geometry="+win.get_window_geometry().toSource()); 335 show("geometry changed"); 336 //show("window_geometry_changed="+win.get_window_geometry().toSource()); 267 337 var geom = win.get_internal_geometry(); 268 var wid = win dow_to_id[win];338 var wid = win.wid; 269 339 if (!win.override_redirect) 270 340 self.set_focus(wid); 271 341 send(["configure-window", wid, geom.x, geom.y, geom.w, geom.h, get_client_properties(win)]); … … 278 348 function mouse_click(win, button, pressed, x, y, modifiers, buttons) { 279 349 "use strict"; 280 350 var wid = window_to_id[win]; 281 //show("mouse_click(..) wid("+win+")="+wid); 282 check_focus(); 351 set_focus(wid); 283 352 send(["button-action", wid, button, pressed, [x, y], modifiers, buttons]); 284 353 }; 285 354 … … 286 355 function check_focus() { 287 356 "use strict"; 288 357 var wid = -1; 289 var win = canvas_state.topOfStack();290 358 if (win != null) 291 359 wid = window_to_id[win]; 292 360 //show("check_focus() window_to_id["+win+"]="+wid+", focus="+wid+", window_to_id="+window_to_id.toSource()); … … 298 366 "use strict"; 299 367 focus = wid; 300 368 send(["focus", focus, []]); 369 topwindow = wid; 301 370 //set the focused flag on all windows: 302 371 var win; 303 372 for (var i in id_to_window) { 304 373 win = id_to_window[i]; 305 374 win.focused = (i==wid); 375 win.updateFocus(); 306 376 } 307 377 } 308 378 379 function refresh_window(wid) { 380 "use strict"; 381 // refresh the window 382 show("send buffer-refresh"); 383 send(['buffer-refresh', wid, true, 95]); 384 } 385 309 386 function get_client_properties(win) { 310 387 "use strict"; 311 388 var cp = win.client_properties; … … 315 392 316 393 function make_new_window(wid, x, y, w, h, metadata, override_redirect, client_properties) { 317 394 "use strict"; 318 var win = new XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, 319 window_geometry_changed, mouse_move, mouse_click, window_closed); 320 //show("make_new_window("+wid+", "+x+", "+y+", "+w+", "+h+", "+metadata+", "+override_redirect+", "+client_properties+")="+win); 395 // each window needs their own DIV that contains a canvas 396 var mydiv = document.createElement("div"); 397 mydiv.id = String(wid); 398 var mycanvas = document.createElement("canvas"); 399 mydiv.appendChild(mycanvas); 400 document.body.appendChild(mydiv); 401 // set initial sizes 402 mycanvas.width = w; 403 mycanvas.height = h; 404 // create the XpraWindow object to own the new div 405 var win = new XpraWindow(mycanvas, wid, x, y, w, h, metadata, override_redirect, client_properties, 406 window_geometry_changed, mouse_move, mouse_click, set_focus, window_closed); 321 407 id_to_window[wid] = win; 322 408 window_to_id[win] = wid; 323 409 var geom = win.get_internal_geometry(); … … 414 500 var win = id_to_window[wid]; 415 501 if (win!=null) { 416 502 win.destroy(); 417 check_focus();418 503 } 419 504 } 420 505 … … 423 508 var wid = packet[1]; 424 509 var win = id_to_window[wid]; 425 510 if (win!=null) { 426 canvas_state.raiseShape(win); 427 focus = wid; 428 check_focus(); 511 set_focus(wid); 429 512 } 430 513 } 431 514 432 515 function process_window_resized(packet) { 433 //TODO 516 var wid = packet[1]; 517 var width = packet[2]; 518 var height = packet[3]; 519 var win = id_to_window[wid]; 520 if (win!=null) { 521 win.resize(width, height); 522 } 434 523 } 524 435 525 function process_new_tray(packet) { 436 526 //TODO 437 527 } … … 462 552 return 96; 463 553 } 464 554 465 466 555 function get_desktop_size() { 467 556 "use strict"; 468 var canvas = document.getElementById('screen'); 469 return [canvas.width, canvas.height]; 557 return [screen_width, screen_height]; 470 558 } 471 559 function get_screen_sizes() { 472 560 "use strict"; 473 var canvas = document.getElementById('screen');474 561 var dpi = get_DPI(); 475 562 /* 476 563 equivallent GTK code: … … 482 569 monitors, 483 570 work_x, work_y, work_width, work_height) 484 571 */ 485 var wmm = Math.round( canvas.width*25.4/dpi);486 var hmm = Math.round( canvas.height*25.4/dpi);487 var monitor = ["Canvas", 0, 0, canvas.width, canvas.height, wmm, hmm];488 var screen = ["HTML", canvas.width, canvas.height,572 var wmm = Math.round(screen_width*25.4/dpi); 573 var hmm = Math.round(screen_height*25.4/dpi); 574 var monitor = ["Canvas", 0, 0, screen_width, screen_height, wmm, hmm]; 575 var screen = ["HTML", screen_width, screen_height, 489 576 wmm, hmm, 490 577 [monitor], 491 0, 0, canvas.width, canvas.height578 0, 0, screen_width, screen_height 492 579 ]; 493 580 //just a single screen: 494 581 return [screen]; … … 593 680 594 681 function do_send_new_screen_size() { 595 682 //show("do_send_new_screen_size()"); 596 if (protocol==null || canvas_state==null)683 if (protocol==null) 597 684 return; 598 685 screen_size_change_pending = false; 599 686 var ds = get_desktop_size(); … … 607 694 } 608 695 } 609 696 610 function onresize() {611 var container = document.getElementById('screen_container');612 var d_form = document.getElementById('disconnect_form');613 var c_form = document.getElementById('connect_form');614 /*show("onresize() container size: "+container.clientWidth+"x"+container.clientHeight+615 ", window size: "+window.innerWidth+"x"+window.innerHeight+616 ", d form size: "+d_form.offsetWidth+"x"+d_form.offsetHeight+617 ", c form size: "+c_form.offsetWidth+"x"+c_form.offsetHeight);*/618 if (canvas_state!=null) {619 var width = Math.min(container.clientWidth, window.innerWidth);620 var height = Math.max(0, window.innerHeight-d_form.offsetHeight-c_form.offsetHeight);621 canvas_state.canvas.width = width;622 canvas_state.canvas.height = height;623 canvas_state.width = width;624 canvas_state.height = height;625 canvas_state.invalidate();626 }627 send_new_screen_size();628 }629 630 697 function do_start(params) { 631 698 "use strict"; 632 699 show("start()"); 633 onresize();634 700 635 701 //hook document and window events: 636 702 document.onkeydown = onkeydown; 637 703 document.onkeyup = onkeyup; 638 704 document.onkeypress = onkeypress; 639 window.addEventListener("resize", onresize, false);640 641 var canvas = document.getElementById('screen');642 canvas.style.display = "block";643 644 if (canvas_state==null)645 canvas_state = new CanvasState(canvas);646 705 protocol = new Protocol(); 647 706 protocol.set_packet_handler("open", process_open); 648 707 protocol.set_packet_handler("close", process_close); … … 657 716 show("opening connection to "+uri); 658 717 set_ui_message("connecting to "+uri); 659 718 protocol.open(uri); 719 // dismiss connection dialog 720 $('#connectModal').modal('hide'); 660 721 } 661 722 catch (e) { 662 723 set_ui_message("error connecting to "+uri+": "+e, "red"); … … 672 733 document.onkeydown = null; 673 734 document.onkeyup = null; 674 735 document.onkeypress = null; 675 window.removeEventListener("resize", onresize);676 736 if (protocol!=null) { 677 737 protocol.close(); 678 738 protocol = null; 679 739 } 680 if (canvas_state!=null) { 681 canvas_state.destroy(); 682 canvas_state = null; 683 } 740 684 741 document.getElementById('connect_form').style.display = "block"; 685 742 document.getElementById('disconnect_form').style.display = "none"; 686 743 687 var canvas = document.getElementById('screen');688 canvas.style.display = "none";689 744 status = "ready"; 690 745 } 691 746 … … 714 769 document.getElementById('disconnect_form').style.display = "block"; 715 770 else 716 771 document.getElementById('disconnect_form').style.display = "none"; 717 onresize();718 772 set_ui_message(""); 719 773 720 774 show("connected to server - sending hello"); … … 848 902 else 849 903 do_start(params); 850 904 } 905 906 $('#connectModal').modal('show'); 851 907 } 852 908 </script> 853 909 </head> … … 877 933 <td><input id="options" type="checkbox" /></td> 878 934 </tr> 879 935 </table> 880 <button id="connect" value="true" onclick="start();">Connect</button>881 <button id="popup" value="true" onclick="new_popup();">Popup</button>882 936 </fieldset> 883 </form> 937 <button id="connect" value="true" class="btn btn-primary" onclick="start();">Connect</button> 938 </form> 884 939 </div> 940 885 941 <div class="form" id="disconnect_form"> 886 942 <form onsubmit="return false;"> 887 943 <fieldset> … … 891 947 </fieldset> 892 948 </form> 893 949 </div> 894 <div id="screen_container">895 <canvas id="screen" width="800" height="600"></canvas>896 </div>897 950 <div id="dpi" style="width: 1in; height: 1in; left: -100%; top: -100%; position: absolute;"> 898 951 </div> 899 952 </body>