diff --git a/RemoteDesktop/README.md b/RemoteDesktop/README.md index 9dad03b..988bb32 100644 --- a/RemoteDesktop/README.md +++ b/RemoteDesktop/README.md @@ -7,6 +7,7 @@ Further information on **wvnc**: [https://blog.lxsang.me/post/id/23](https://blo ## Change logs +* v0.1.7-8 - remove package dependencies, use web assembly for jpeg decoding, improve rendering performance and connection stability * v0.1.6 - Change category * v0.1.5 - add package dependencies and use the new **libwvnc** * v0.1.0 - adapt to the new AntOS API diff --git a/RemoteDesktop/build/debug/README.md b/RemoteDesktop/build/debug/README.md index 9dad03b..988bb32 100644 --- a/RemoteDesktop/build/debug/README.md +++ b/RemoteDesktop/build/debug/README.md @@ -7,6 +7,7 @@ Further information on **wvnc**: [https://blog.lxsang.me/post/id/23](https://blo ## Change logs +* v0.1.7-8 - remove package dependencies, use web assembly for jpeg decoding, improve rendering performance and connection stability * v0.1.6 - Change category * v0.1.5 - add package dependencies and use the new **libwvnc** * v0.1.0 - adapt to the new AntOS API diff --git a/RemoteDesktop/build/debug/main.js b/RemoteDesktop/build/debug/main.js index fd4240e..447a753 100644 --- a/RemoteDesktop/build/debug/main.js +++ b/RemoteDesktop/build/debug/main.js @@ -1 +1 @@ -(function(){var e,t,n,a;(e=class e extends this.OS.GUI.BasicDialog{constructor(){super("ConnectionDialog",e.scheme)}main(){return super.main(),this.find("jq").value=40,this.find("bt-ok").onbtclick=e=>{var t;if(this.handle)return t={wvnc:this.find("txtWVNC").value,server:this.find("txtServer").value,quality:this.find("jq").value},this.handle(t),this.quit()},this.find("bt-cancel").onbtclick=e=>this.quit()}}).scheme='\n \n
\n \n \n \n \n \n
\n \n \n \n
\n \n \n
\n
\n
\n
\n
\n
\n
\n',(t=class e extends this.OS.GUI.BasicDialog{constructor(){super("CredentialDialog",e.scheme)}main(){return this.find("bt-ok").onbtclick=()=>{var e;return this.handle?(e={username:this.find("txtUser").value,password:this.find("txtPass").value},this.handle(e),this.quit()):this.quit()},this.find("bt-cancel").onbtclick=()=>this.quit()}}).scheme='\n \n \n \n \n \n \n
\n \n \n
\n
\n
\n
\n
',n=class extends this.OS.application.BaseApplication{constructor(e){super("RemoteDesktop",e)}main(){return this.canvas=this.find("screen"),this.container=this.find("container"),this.client=new a({element:this.canvas}),this.client.onerror=e=>(this.error(e),this.showConnectionDialog()),this.client.onresize=()=>this.setScale(),this.client.onpassword=()=>new Promise((e,t)=>this.openDialog("PromptDialog",{title:__("VNC password"),label:__("VNC password"),value:"password",type:"password"}).then((function(t){return e(t)}))),this.client.oncredential=()=>new Promise((e,n)=>this.openDialog(new t,{title:__("User credential")}).then((function(t){return e(t.username,t.password)}))),this.on("resize",e=>this.setScale()),this.on("focus",e=>$(this.canvas).focus()),this.client.init().then(()=>this.showConnectionDialog())}setScale(){var e,t,n,a;if(this.client&&this.client.resolution)return a=$(this.container).width(),e=$(this.container).height(),(t=a/this.client.resolution.w)>(n=e/this.client.resolution.h)?this.client.setScale(n):this.client.setScale(t)}menu(){return[{text:"__(Connection)",nodes:[{text:"__(New Connection)",dataid:this.name+"-new"},{text:"__(Disconnect)",dataid:this.name+"-close"}],onchildselect:e=>this.actionConnection()}]}actionConnection(e){return this.client&&this.client.disconnect(!1),this.showConnectionDialog()}showConnectionDialog(){return this.openDialog(new e,{title:__("Connection")}).then(e=>(this.client.ws=e.wvnc,this.client.connect(e.server,e)))}cleanup(){if(this.client)return this.client.disconnect(!0)}},this.OS.register("RemoteDesktop",n),a=class{constructor(e){var t,n;this.socket=void 0,this.ws=void 0,this.canvas=void 0,n="pkg://RemoteDesktop/decoder_asm.js".asFileHandle().getlink(),this.scale=1,e.ws&&(this.ws=e.ws),this.canvas=e.element,"string"==typeof this.canvas&&(this.canvas=document.getElementById(this.canvas)),this.decoder=new Worker(n),this.enableEvent=!0,t=this,this.mouseMask=0,this.decoder.onmessage=function(e){return t.process(e.data)}}init(){var e;return e=this,new Promise((function(t,n){return e.canvas?($(e.canvas).attr("tabindex","1"),e.initInputEvent(),t()):n("Canvas is not set")}))}initInputEvent(){var e,t,n,a;if(n=this,this.canvas&&(t=function(e){var t;return t=n.canvas.getBoundingClientRect(),{x:Math.floor((e.clientX-t.left)/n.scale),y:Math.floor((e.clientY-t.top)/n.scale)}},a=function(e){var a;if(n.enableEvent)return a=t(e),n.sendPointEvent(a.x,a.y,n.mouseMask)},n.canvas))return n.canvas.oncontextmenu=function(e){return e.preventDefault(),!1},n.canvas.onmousemove=function(e){return a(e)},n.canvas.onmousedown=function(e){var t;return t=1<this.disconnect(!0),window.addEventListener("unload",e),window.addEventListener("beforeunload",e)}initCanvas(e,t,n){return this.depth=n,this.canvas.width=e,this.canvas.height=t,this.resolution={w:e,h:t,depth:this.depth},this.decoder.postMessage(this.resolution),this.setScale(this.scale)}process(e){var t,n,a;if(this.socket)return n=new Uint8Array(e.pixels),(a=(t=this.canvas.getContext("2d",{alpha:!1})).createImageData(e.w,e.h)).data.set(n),t.putImageData(a,e.x,e.y)}setScale(e){if(this.scale=e,this.canvas)return this.canvas.style.transformOrigin="0 0",this.canvas.style.transform="scale("+e+")"}connect(e,t){var n;if(n=this,this.disconnect(!1),this.ws)return this.socket=new WebSocket(this.ws),this.socket.binaryType="arraybuffer",this.socket.onopen=function(){return console.log("socket opened"),n.initConnection(e,t)},this.socket.onmessage=function(e){return n.consume(e)},this.socket.onclose=function(){return n.socket=null,n.canvas.style.cursor="auto",n.canvas&&n.resolution&&n.canvas.getContext("2d").clearRect(0,0,n.resolution.w,n.resolution.h),console.log("socket closed")}}disconnect(e){if(this.socket&&this.socket.close(),this.socket=void 0,e)return this.decoder.terminate()}initConnection(e,t){var n;return(n=new Uint8Array(e.length+1))[0]=50,t&&t.quality&&(n[0]=t.quality),n.set((new TextEncoder).encode(e),1),this.socket.send(this.buildCommand(1,n))}sendPointEvent(e,t,n){var a;if(this.socket)return(a=new Uint8Array(5))[0]=255&e,a[1]=e>>8,a[2]=255&t,a[3]=t>>8,a[4]=n,this.socket.send(this.buildCommand(5,a))}sendKeyEvent(e,t){var n;if(this.socket&&this.enableEvent)return(n=new Uint8Array(3))[0]=255&e,n[1]=e>>8,n[2]=t,this.socket.send(this.buildCommand(6,n))}buildCommand(e,t){var n,a;switch(a=void 0,typeof t){case"string":a=(new TextEncoder).encode(t);break;case"number":a=new Uint8Array([t]);break;default:a=t}return(n=new Uint8Array(a.length+3))[0]=e,n[2]=a.length>>8,n[1]=15&a.length,n.set(a,3),n.buffer}oncopy(e){return console.log("Get clipboard text: "+e)}onpassword(){return new Promise((function(e,t){return t("onpassword is not implemented")}))}sendTextAsClipboard(e){if(this.socket)return console.log("send ",e),this.socket.send(this.buildCommand(7,e))}oncredential(){return new Promise((function(e,t){return t("oncredential is not implemented")}))}onerror(e){return console.log("Error",e)}onresize(){return console.log("resize")}consume(e){var t,n,a,s,i,o;switch(t=(n=new Uint8Array(e.data))[0],i=this,t){case 254:return n=n.subarray(1,n.length-1),a=new TextDecoder("utf-8"),this.onerror(a.decode(n));case 129:return console.log("Request for password"),this.enableEvent=!1,this.onpassword().then((function(e){return i.socket.send(i.buildCommand(2,e)),i.enableEvent=!0}));case 130:return console.log("Request for login"),this.enableEvent=!1,this.oncredential().then((function(e,t){var n;return(n=new Uint8Array(e.length+t.length+1)).set((new TextEncoder).encode(e),0),n.set(["\0"],e.length),n.set((new TextEncoder).encode(t),e.length+1),i.socket.send(i.buildCommand(3,n)),i.enableEvent=!0}));case 131:return o=n[1]|n[2]<<8,s=n[3]|n[4]<<8,this.initCanvas(o,s,32),this.socket.send(this.buildCommand(4,1)),this.onresize();case 132:return this.decoder.postMessage(n.buffer,[n.buffer]),this.socket.send(this.buildCommand(4,1));case 133:return n=n.subarray(1),a=new TextDecoder("utf-8"),this.oncopy(a.decode(n)),this.socket.send(this.buildCommand(4,1));default:return console.log(t)}}},window.WVNC=a}).call(this); \ No newline at end of file +(function(){var e,t,n,s;(e=class e extends this.OS.GUI.BasicDialog{constructor(){super("ConnectionDialog",e.scheme)}main(){return super.main(),this.find("jq").value=40,this.find("bt-ok").onbtclick=e=>{var t;if(this.handle)return t={wvnc:this.find("txtWVNC").value,server:this.find("txtServer").value,quality:this.find("jq").value},this.handle(t),this.quit()},this.find("bt-cancel").onbtclick=e=>this.quit()}}).scheme='\n \n
\n \n \n \n \n \n
\n \n \n \n
\n \n \n
\n
\n
\n
\n
\n
\n
\n',(t=class e extends this.OS.GUI.BasicDialog{constructor(){super("CredentialDialog",e.scheme)}main(){return this.find("bt-ok").onbtclick=()=>{var e;return this.handle?(e={username:this.find("txtUser").value,password:this.find("txtPass").value},this.handle(e),this.quit()):this.quit()},this.find("bt-cancel").onbtclick=()=>this.quit()}}).scheme='\n \n \n \n \n \n \n
\n \n \n
\n
\n
\n
\n
',n=class extends this.OS.application.BaseApplication{constructor(e){super("RemoteDesktop",e)}main(){return this.canvas=this.find("screen"),this.container=this.find("container"),this.client=new s({element:this.canvas}),this.client.onerror=e=>(this.error(e),this.showConnectionDialog()),this.client.onresize=()=>this.setScale(),this.client.onpassword=()=>new Promise((e,t)=>this.openDialog("PromptDialog",{title:__("VNC password"),label:__("VNC password"),value:"password",type:"password"}).then((function(t){return e(t)}))),this.client.oncredential=()=>new Promise((e,n)=>this.openDialog(new t,{title:__("User credential")}).then((function(t){return e(t.username,t.password)}))),this.on("resize",e=>this.setScale()),this.on("focus",e=>$(this.canvas).focus()),this.client.init().then(()=>this.showConnectionDialog())}setScale(){var e,t,n,s;if(this.client&&this.client.resolution)return s=$(this.container).width(),e=$(this.container).height(),(t=s/this.client.resolution.w)>(n=e/this.client.resolution.h)?this.client.setScale(n):this.client.setScale(t)}menu(){return[{text:"__(Connection)",nodes:[{text:"__(New Connection)",dataid:this.name+"-new"},{text:"__(Disconnect)",dataid:this.name+"-close"}],onchildselect:e=>this.actionConnection()}]}actionConnection(e){return this.client&&this.client.disconnect(!1),this.showConnectionDialog()}showConnectionDialog(){return this.openDialog(new e,{title:__("Connection")}).then(e=>(this.client.ws=e.wvnc,this.client.connect(e.server,e)))}cleanup(){if(this.client)return this.client.disconnect(!0)}},this.OS.register("RemoteDesktop",n),s=class{constructor(e){var t,n;this.socket=void 0,this.ws=void 0,this.canvas=void 0,n="pkg://RemoteDesktop/decoder_asm.js".asFileHandle().getlink(),this.scale=1,e.ws&&(this.ws=e.ws),this.canvas=e.element,"string"==typeof this.canvas&&(this.canvas=document.getElementById(this.canvas)),this.decoder=new Worker(n),this.enableEvent=!1,this.pingto=!1,t=this,this.mouseMask=0,this.decoder.onmessage=function(e){return t.process(e.data)}}init(){var e;return e=this,new Promise((function(t,n){return e.canvas?($(e.canvas).attr("tabindex","1"),$(e.canvas).on("focus",()=>e.resetModifierKeys()),e.initInputEvent(),t()):n("Canvas is not set")}))}initInputEvent(){var e,t,n,s;if(n=this,this.canvas&&(t=function(e){var t;return t=n.canvas.getBoundingClientRect(),{x:Math.floor((e.clientX-t.left)/n.scale),y:Math.floor((e.clientY-t.top)/n.scale)}},s=function(e){var s;if(n.enableEvent)return s=t(e),n.sendPointEvent(s.x,s.y,n.mouseMask)},n.canvas))return n.canvas.oncontextmenu=function(e){return e.preventDefault(),!1},n.canvas.onmousemove=function(e){return s(e)},n.canvas.onmousedown=function(e){var t;return t=1<this.disconnect(!0),window.addEventListener("unload",e),window.addEventListener("beforeunload",e)}initCanvas(e,t,n){return this.depth=n,this.canvas.width=e,this.canvas.height=t,this.resolution={w:e,h:t,depth:this.depth},this.decoder.postMessage(this.resolution),this.setScale(this.scale)}process(e){var t,n,s;if(this.socket)return n=new Uint8Array(e.pixels),(s=(t=this.canvas.getContext("2d",{alpha:!1})).createImageData(e.w,e.h)).data.set(n),t.putImageData(s,e.x,e.y)}setScale(e){if(this.scale=e,this.canvas)return this.canvas.style.transformOrigin="0 0",this.canvas.style.transform="scale("+e+")"}connect(e,t){var n;if(n=this,this.disconnect(!1),this.ws)return this.socket=new WebSocket(this.ws),this.socket.binaryType="arraybuffer",this.socket.onopen=function(){return console.log("socket opened"),n.initConnection(e,t)},this.socket.onmessage=function(e){return n.consume(e)},this.socket.onclose=function(){return n.socket=null,n.canvas.style.cursor="auto",n.canvas&&n.resolution&&n.canvas.getContext("2d").clearRect(0,0,n.resolution.w,n.resolution.h),n.pingto&&clearTimeout(n.pingto),n.pingto=void 0,console.log("socket closed")}}disconnect(e){return this.socket&&this.socket.close(),this.socket=void 0,e&&this.decoder.terminate(),this.enableEvent=!1}initConnection(e,t){var n;return(n=new Uint8Array(e.length+1))[0]=50,t&&t.quality&&(n[0]=t.quality),n.set((new TextEncoder).encode(e),1),this.socket.send(this.buildCommand(1,n))}resetModifierKeys(){if(this.socket&&this.enableEvent)return this.sendKeyEvent(65511,0),this.sendKeyEvent(65512,0),this.sendKeyEvent(65505,0),this.sendKeyEvent(65507,0),this.sendKeyEvent(65513,0)}sendPointEvent(e,t,n){var s;if(this.socket&&this.enableEvent)return(s=new Uint8Array(5))[0]=255&e,s[1]=e>>8,s[2]=255&t,s[3]=t>>8,s[4]=n,this.socket.send(this.buildCommand(5,s))}sendKeyEvent(e,t){var n;if(this.socket&&this.enableEvent)return(n=new Uint8Array(3))[0]=255&e,n[1]=e>>8&255,n[2]=t,this.socket.send(this.buildCommand(6,n))}sendPing(){if(this.socket)return this.socket.send(this.buildCommand(8,"PING WVNC"))}buildCommand(e,t){var n,s;switch(s=void 0,typeof t){case"string":s=(new TextEncoder).encode(t);break;case"number":s=new Uint8Array([t]);break;default:s=t}return(n=new Uint8Array(s.length+3))[0]=e,n[2]=s.length>>8,n[1]=15&s.length,n.set(s,3),n.buffer}oncopy(e){return console.log("Get clipboard text: "+e)}onpassword(){return new Promise((function(e,t){return t("onpassword is not implemented")}))}sendTextAsClipboard(e){if(this.socket)return console.log("send ",e),this.socket.send(this.buildCommand(7,e))}oncredential(){return new Promise((function(e,t){return t("oncredential is not implemented")}))}onerror(e){return console.log("Error",e)}onresize(){return console.log("resize")}consume(e){var t,n,s,i,a,o,r;switch(t=(n=new Uint8Array(e.data))[0],o=this,t){case 254:return n=n.subarray(1,n.length-1),s=new TextDecoder("utf-8"),this.onerror(s.decode(n));case 129:return console.log("Request for password"),this.enableEvent=!1,this.onpassword().then((function(e){return o.socket.send(o.buildCommand(2,e)),o.enableEvent=!0}));case 130:return console.log("Request for login"),this.enableEvent=!1,this.oncredential().then((function(e,t){var n;return(n=new Uint8Array(e.length+t.length+1)).set((new TextEncoder).encode(e),0),n.set(["\0"],e.length),n.set((new TextEncoder).encode(t),e.length+1),o.socket.send(o.buildCommand(3,n)),o.enableEvent=!0}));case 131:if(r=n[1]|n[2]<<8,a=n[3]|n[4]<<8,this.initCanvas(r,a,32),this.socket.send(this.buildCommand(4,1)),this.enableEvent=!0,this.onresize(),this.pingto)return;return i=()=>(this.sendPing(),this.pingto=setTimeout(i,5e3)),this.pingto=setTimeout(i,5e3);case 132:return this.decoder.postMessage(n.buffer,[n.buffer]),this.socket.send(this.buildCommand(4,1));case 133:return n=n.subarray(1),s=new TextDecoder("utf-8"),this.oncopy(s.decode(n)),this.socket.send(this.buildCommand(4,1));default:return console.log(t)}}},window.WVNC=s}).call(this); \ No newline at end of file diff --git a/RemoteDesktop/build/debug/package.json b/RemoteDesktop/build/debug/package.json index 4ae4afd..caf57c9 100644 --- a/RemoteDesktop/build/debug/package.json +++ b/RemoteDesktop/build/debug/package.json @@ -7,7 +7,7 @@ "author": "Dany LE", "email": "contact@iohub.dev" }, - "version":"0.1.7-b", + "version":"0.1.8-b", "dependencies": [], "category":"Internet", "icon": "icon.png", diff --git a/RemoteDesktop/build/release/RemoteDesktop.zip b/RemoteDesktop/build/release/RemoteDesktop.zip index 06c662e..cee9ece 100644 Binary files a/RemoteDesktop/build/release/RemoteDesktop.zip and b/RemoteDesktop/build/release/RemoteDesktop.zip differ diff --git a/RemoteDesktop/coffees/WVNC.coffee b/RemoteDesktop/coffees/WVNC.coffee index 31a6576..db83c53 100644 --- a/RemoteDesktop/coffees/WVNC.coffee +++ b/RemoteDesktop/coffees/WVNC.coffee @@ -9,7 +9,8 @@ class WVNC @canvas = args.element @canvas = document.getElementById @canvas if typeof @canvas is 'string' @decoder = new Worker worker - @enableEvent = true + @enableEvent = false + @pingto = false me = @ @mouseMask = 0 @decoder.onmessage = (e) -> @@ -20,6 +21,8 @@ class WVNC return e('Canvas is not set') if not me.canvas # fix keyboard event problem $(me.canvas).attr 'tabindex', '1' + $(me.canvas).on "focus", () => + me.resetModifierKeys() me.initInputEvent() r() @@ -64,7 +67,7 @@ class WVNC #console.log e switch keycode when 8 then code = 0xFF08 #back space - when 9 then code = 0xff89 #0xFF09 # tab ? + when 9 then code = 0xFF09 # tab ? 0xff89 when 13 then code = 0xFF0D # return when 27 then code = 0xFF1B # esc when 46 then code = 0xFFFF # delete to verify @@ -180,12 +183,15 @@ class WVNC me.socket = null me.canvas.style.cursor = "auto" me.canvas.getContext('2d').clearRect 0,0, me.resolution.w, me.resolution.h if me.canvas and me.resolution + clearTimeout(me.pingto) if me.pingto + me.pingto = undefined console.log "socket closed" disconnect: (close_worker) -> @socket.close() if @socket @socket = undefined @decoder.terminate() if close_worker + @enableEvent = false initConnection: (vncserver, params) -> #vncserver = "192.168.1.20:5901" @@ -198,8 +204,18 @@ class WVNC data.set (new TextEncoder()).encode(vncserver), 1 @socket.send(@buildCommand 0x01, data) + resetModifierKeys: () -> + return unless @socket + return unless @enableEvent + @sendKeyEvent 0xFFE7, 0 # meta left + @sendKeyEvent 0xFFE8, 0 # meta right + @sendKeyEvent 0xFFE1, 0 # shift left + @sendKeyEvent 0xFFE3, 0 # ctrl left + @sendKeyEvent 0xFFE9, 0 # alt left + sendPointEvent: (x, y, mask) -> return unless @socket + return unless @enableEvent data = new Uint8Array 5 data[0] = x & 0xFF data[1] = x >> 8 @@ -209,14 +225,18 @@ class WVNC @socket.send( @buildCommand 0x05, data ) sendKeyEvent: (code, v) -> - #console.log code, v + # console.log code, v return unless @socket return unless @enableEvent data = new Uint8Array 3 data[0] = code & 0xFF - data[1] = code >> 8 + data[1] = (code >> 8) & 0xFF data[2] = v @socket.send( @buildCommand 0x06, data ) + + sendPing: () -> + return unless @socket + @socket.send( @buildCommand 0x08, 'PING WVNC' ) buildCommand: (hex, o) -> data = undefined @@ -289,7 +309,14 @@ class WVNC @initCanvas w, h, depth # status command for ack @socket.send(@buildCommand 0x04, 1) + @enableEvent = true + # @resetModifierKeys() @onresize() + return if @pingto + fn = () => + @sendPing() + @pingto = setTimeout(fn, 5000) + @pingto = setTimeout(fn, 5000) when 0x84 # send data to web assembly for decoding @decoder.postMessage data.buffer, [data.buffer] diff --git a/RemoteDesktop/package.json b/RemoteDesktop/package.json index 4ae4afd..caf57c9 100644 --- a/RemoteDesktop/package.json +++ b/RemoteDesktop/package.json @@ -7,7 +7,7 @@ "author": "Dany LE", "email": "contact@iohub.dev" }, - "version":"0.1.7-b", + "version":"0.1.8-b", "dependencies": [], "category":"Internet", "icon": "icon.png", diff --git a/packages.json b/packages.json index 09bd2a1..ee8b95b 100644 --- a/packages.json +++ b/packages.json @@ -365,7 +365,7 @@ "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteDesktop/README.md", "category": "Internet", "author": "Dany LE", - "version": "0.1.7-b", + "version": "0.1.8-b", "dependencies": [],"category":"Internet","icon":"icon.png","mimes":["none"], "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteDesktop/build/release/RemoteDesktop.zip" },