+
@@ -692,7 +692,7 @@ function renderAsk() {
// 需要處理 (a step only you can do on a computer): its screen is one tap away, then 我完成了 / 跳過.
if (a.kind === "handoff") {
$("ask").innerHTML = `
`;
}
@@ -737,36 +737,33 @@ function renderChips() {
// ---------- screens ----------
// Each bot's own computer goes by the bot's name (「Master 的電腦」).
function ownComputer() { const n = botName(S.open); return n ? n + " 的電腦" : "Bot 的電腦"; }
-$("b-screen").onclick = $("b-screen2").onclick = () => { S.back = S.view; show("screen"); $("seg").children[0].textContent = ownComputer(); $("seg").children[1].style.display = S.mac ? "" : "none"; startFrames(); };
+// The bots' computer: live in the viewer page (VNC), where you can also take over.
+function openBox(control) {
+ const at = S.relay ? "host=" + encodeURIComponent(S.host) : "lan=1";
+ location.href = "screen#" + at + "&name=" + encodeURIComponent(ownComputer()) + (control ? "&control=1" : "");
+}
+$("b-screen").onclick = $("b-screen2").onclick = () => openBox(false);
+$("m-mac").onclick = () => { $("sheet").classList.remove("on"); S.back = S.view; S.screen = "mac"; show("screen"); startFrames(); };
$("b-back2").onclick = () => show(S.back === "chat" ? "chat" : "list");
-document.querySelectorAll("#seg button").forEach((b) => b.onclick = () => { document.querySelectorAll("#seg button").forEach((x) => x.classList.toggle("on", x === b)); S.screen = b.dataset.w; $("k-wake").style.display = S.screen === "box" ? "" : "none"; startFrames(); });
-// The bots' computer sends a picture the moment its screen changes: ask for the one after the
-// picture shown (X-Seq), again as soon as it arrives. (It was one picture a second, so a tap
-// showed its result a second or two later.) This computer's own screen: a picture at a time.
+// This Mac's screen: a picture at a time.
let lastUrl = null;
const nap = (ms) => new Promise((r) => setTimeout(r, ms));
function startFrames() { stopFrames(); const run = S.frameRun = {}; frames(run); }
function stopFrames() { S.frameRun = null; }
async function frames(run) {
- let seq = 0;
while (S.frameRun === run) {
if (document.hidden) { await nap(800); continue; }
try {
- const live = S.screen === "box";
- const r = await api(base() + "api/screen/" + S.screen + (live ? "?after=" + seq : ""), {raw: true});
+ const r = await api(base() + "api/screen/mac", {raw: true});
if (S.frameRun !== run) return;
if (!r.ok) { const v = await r.json().catch(() => ({})); $("stage").innerHTML = `
${esc(v.error || "看不到畫面")}
`; $("screen-live").textContent = ""; await nap(1500); continue; }
- const n = +(r.headers.get("X-Seq") || 0);
- if (live && n && n === seq) continue;
- seq = n;
S.size = (r.headers.get("X-Size") || "0x0").split("x").map(Number);
const url = URL.createObjectURL(await r.blob());
let img = $("stage").querySelector("img");
if (!img) { $("stage").innerHTML = ""; img = document.createElement("img"); img.draggable = false; $("stage").appendChild(img); bindStage(img); }
img.src = url; if (lastUrl) URL.revokeObjectURL(lastUrl); lastUrl = url;
$("screen-live").innerHTML = `即時`;
- // At most about 8 pictures a second (an animated page would send all it can).
- await nap(!live || !n ? 700 : 120);
+ await nap(700);
} catch (e) { $("screen-live").textContent = ""; await nap(1500); }
}
}
@@ -788,7 +785,7 @@ function act(img, e, button) {
input2({kind: "click", x: p.x, y: p.y, button});
}
async function input2(body) {
- try { await api(base() + "api/screen/" + S.screen, {json: body}); } catch (e) { toast(e.message); }
+ try { await api(base() + "api/screen/mac", {json: body}); } catch (e) { toast(e.message); }
}
document.querySelectorAll("[data-key]").forEach((b) => b.onclick = () => input2({kind: "key", key: b.dataset.key}));
document.querySelectorAll("[data-scroll]").forEach((b) => b.onclick = () => input2({kind: "scroll", x: Math.round(S.size[0] / 2), y: Math.round(S.size[1] / 2), direction: b.dataset.scroll, amount: 5}));
@@ -796,9 +793,8 @@ $("k-type").onclick = () => { $("typebar").classList.toggle("on"); if ($("typeba
$("b-type").onclick = () => { const t = $("typein").value; if (!t) return; $("typein").value = ""; input2({kind: "type", text: t}); };
$("typein").addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.isComposing) { e.preventDefault(); $("b-type").click(); } });
$("k-right").onclick = () => { S.right = !S.right; $("k-right").textContent = "右鍵:" + (S.right ? "開" : "關"); };
-$("k-wake").onclick = () => { toast("正在叫醒" + ownComputer() + "…"); input2({kind: "wake"}); };
// ---------- menu, notifications ----------
-$("b-more").onclick = () => { $("m-host").style.display = S.relay ? "" : "none"; $("m-pair").style.display = S.relay ? "" : "none"; notifyLabel(); $("sheet").classList.add("on"); };
+$("b-more").onclick = () => { $("m-mac").style.display = S.mac ? "" : "none"; $("m-host").style.display = S.relay ? "" : "none"; $("m-pair").style.display = S.relay ? "" : "none"; notifyLabel(); $("sheet").classList.add("on"); };
$("sheet").onclick = (e) => { if (e.target.id === "sheet") $("sheet").classList.remove("on"); };
$("m-cancel").onclick = () => $("sheet").classList.remove("on");
$("m-out").onclick = () => { $("sheet").classList.remove("on"); if (confirm("要登出這支手機嗎?")) signOut(); };
diff --git a/mobile/novnc.js b/mobile/novnc.js
new file mode 100644
index 0000000..5c01ecd
--- /dev/null
+++ b/mobile/novnc.js
@@ -0,0 +1,8 @@
+/* noVNC 1.6.0 (https://github.com/novnc/noVNC), core library under the Mozilla Public License 2.0
+ (https://mozilla.org/MPL/2.0/; source: the tag v1.6.0, core/rfb.js and what it imports).
+ Bundled for AnyBot with: esbuild core/rfb.js --bundle --minify --format=esm --target=es2022 */
+function ji(i){return i>>>0}function it(i){return i|0}var qi="warn",L=()=>{},V=()=>{},W=()=>{},U=()=>{};function Zr(i){if(typeof i>"u"?i=qi:qi=i,L=V=W=U=()=>{},typeof window.console<"u")switch(i){case"debug":L=console.debug.bind(window.console);case"info":V=console.info.bind(window.console);case"warn":W=console.warn.bind(window.console);case"error":U=console.error.bind(window.console);case"none":break;default:throw new window.Error("invalid logging type '"+i+"'")}}Zr();function Bt(i,t=!1){try{return decodeURIComponent(escape(i))}catch(e){if(e instanceof URIError&&t)return i;throw e}}function he(i){return unescape(encodeURIComponent(i))}var Pe={toBase64Table:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),base64Pad:"=",encode(i){"use strict";let t="",e=i.length,s=e%3;for(let n=0;n>2],t+=this.toBase64Table[((i[n]&3)<<4)+(i[n+1]>>4)],t+=this.toBase64Table[((i[n+1]&15)<<2)+(i[n+2]>>6)],t+=this.toBase64Table[i[n+2]&63];let r=e-s;return s===2?(t+=this.toBase64Table[i[r]>>2],t+=this.toBase64Table[((i[r]&3)<<4)+(i[r+1]>>4)],t+=this.toBase64Table[(i[r+1]&15)<<2],t+=this.toBase64Table[64]):s===1&&(t+=this.toBase64Table[i[r]>>2],t+=this.toBase64Table[(i[r]&3)<<4],t+=this.toBase64Table[64],t+=this.toBase64Table[64]),t},toBinaryTable:[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,0,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1],decode(i,t=0){let e=i.indexOf("=")-t;e<0&&(e=i.length-t);let s=(e>>2)*3+Math.floor(e%4/1.5),r=new Array(s),n=0,a=0;for(let h=0,_=t;_=8&&(n-=8,l||(r[h++]=a>>n&255),a&=(1<0||navigator.msMaxTouchPoints>0;window.addEventListener("touchstart",function i(){_i=!0,window.removeEventListener("touchstart",i,!1)},!1);var st=10*(window.devicePixelRatio||1),Ji=!1;try{let i=document.createElement("canvas");i.style.cursor='url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default',i.style.cursor.indexOf("url")===0?(V("Data URI scheme cursor supported"),Ji=!0):W("Data URI scheme cursor not supported")}catch(i){U("Data URI scheme cursor test exception: "+i)}var $i=Ji,Yr=!0;try{let i=document.createElement("div");i.style.visibility="hidden",i.style.overflow="scroll",document.body.appendChild(i);let t=document.createElement("div");i.appendChild(t);let e=i.offsetWidth-t.offsetWidth;i.parentNode.removeChild(i),Yr=e!=0}catch(i){U("Scrollbar test exception: "+i)}var fi=!1;async function jr(){if(!("VideoDecoder"in window))return!1;let i={codec:"avc1.42401f",codedWidth:1920,codedHeight:1080,optimizeForLatency:!0};if(!(await VideoDecoder.isConfigSupported(i)).supported)return!1;let e=new Uint8Array(Pe.decode("AAAAAWdCwBTZnpuAgICgAAADACAAAAZB4oVNAAAAAWjJYyyAAAABBgX//4HcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTY0IHIzMTA4IDMxZTE5ZjkgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDIzIC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1sIC0gb3B0aW9uczogY2FiYWM9MCByZWY9NSBkZWJsb2NrPTE6MDowIGFuYWx5c2U9MHgxOjB4MTExIG1lPWhleCBzdWJtZT04IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0yIDh4OGRjdD0wIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MSBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTAgd2VpZ2h0cD0wIGtleWludD1pbmZpbml0ZSBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NTAgcmM9YWJyIG1idHJlZT0xIGJpdHJhdGU9NDAwIHJhdGV0b2w9MS4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAABZYiEBrxmKAAPVccAAS044AA5DRJMnkycJk4TPw==")),s=!1,r=null,n=new VideoDecoder({output:h=>{s=!0},error:h=>{r=h}}),a=new EncodedVideoChunk({timestamp:0,type:"key",data:e});n.configure(i),n.decode(a);try{await n.flush()}catch(h){r=h}return!(!s||r!==null)}fi=await jr();function me(){return!!/mac/i.exec(navigator.platform)}function He(){return!!/win/i.exec(navigator.platform)}function Ue(){return!!/ipad/i.exec(navigator.platform)||!!/iphone/i.exec(navigator.platform)||!!/ipod/i.exec(navigator.platform)}function Ne(i,t,e){let s=e.getBoundingClientRect(),r={x:0,y:0};return i=s.right?r.x=s.width-1:r.x=i-s.left,t=s.bottom?r.y=s.height-1:r.y=t-s.top,r}function Se(i){i.stopPropagation(),i.preventDefault()}var ui=!1,ts=null;document.captureElement=null;function ke(i){if(ui)return;let t=new i.constructor(i.type,i);ui=!0,document.captureElement?document.captureElement.dispatchEvent(t):ts.dispatchEvent(t),ui=!1,i.stopPropagation(),t.defaultPrevented&&i.preventDefault(),i.type==="mouseup"&&ns()}function is(){let i=document.getElementById("noVNC_mouse_capture_elem");i.style.cursor=window.getComputedStyle(document.captureElement).cursor}var ss=new MutationObserver(is);function rs(i){if(i.setCapture)i.setCapture(),document.captureElement=i;else{ns();let t=document.getElementById("noVNC_mouse_capture_elem");t===null&&(t=document.createElement("div"),t.id="noVNC_mouse_capture_elem",t.style.position="fixed",t.style.top="0px",t.style.left="0px",t.style.width="100%",t.style.height="100%",t.style.zIndex=1e4,t.style.display="none",document.body.appendChild(t),t.addEventListener("contextmenu",ke),t.addEventListener("mousemove",ke),t.addEventListener("mouseup",ke)),document.captureElement=i,ss.observe(i,{attributes:!0}),is(),t.style.display="",window.addEventListener("mousemove",ke),window.addEventListener("mouseup",ke)}}function ns(){if(document.releaseCapture)document.releaseCapture(),document.captureElement=null;else{if(!document.captureElement)return;ts=document.captureElement,document.captureElement=null,ss.disconnect();let i=document.getElementById("noVNC_mouse_capture_elem");i.style.display="none",window.removeEventListener("mousemove",ke),window.removeEventListener("mouseup",ke)}}var Ee=class{constructor(){this._listeners=new Map}addEventListener(t,e){this._listeners.has(t)||this._listeners.set(t,new Set),this._listeners.get(t).add(e)}removeEventListener(t,e){this._listeners.has(t)&&this._listeners.get(t).delete(e)}dispatchEvent(t){return this._listeners.has(t.type)?(this._listeners.get(t.type).forEach(e=>e.call(this,t)),!t.defaultPrevented):!0}};var rt=class{constructor(t){if(this._drawCtx=null,this._renderQ=[],this._flushPromise=null,this._fbWidth=0,this._fbHeight=0,this._prevDrawStyle="",L(">> Display.constructor"),this._target=t,!this._target)throw new Error("Target must be set");if(typeof this._target=="string")throw new Error("target must be a DOM element");if(!this._target.getContext)throw new Error("no getContext method");this._targetCtx=this._target.getContext("2d"),this._viewportLoc={x:0,y:0,w:this._target.width,h:this._target.height},this._backbuffer=document.createElement("canvas"),this._drawCtx=this._backbuffer.getContext("2d"),this._damageBounds={left:0,top:0,right:this._backbuffer.width,bottom:this._backbuffer.height},L("User Agent: "+navigator.userAgent),L("<< Display.constructor"),this._scale=1,this._clipViewport=!1}get scale(){return this._scale}set scale(t){this._rescale(t)}get clipViewport(){return this._clipViewport}set clipViewport(t){this._clipViewport=t;let e=this._viewportLoc;this.viewportChangeSize(e.w,e.h),this.viewportChangePos(0,0)}get width(){return this._fbWidth}get height(){return this._fbHeight}viewportChangePos(t,e){let s=this._viewportLoc;t=Math.floor(t),e=Math.floor(e),this._clipViewport||(t=-s.w,e=-s.h);let r=s.x+s.w-1,n=s.y+s.h-1;t<0&&s.x+t<0&&(t=-s.x),r+t>=this._fbWidth&&(t-=r+t-this._fbWidth+1),s.y+e<0&&(e=-s.y),n+e>=this._fbHeight&&(e-=n+e-this._fbHeight+1),!(t===0&&e===0)&&(L("viewportChange deltaX: "+t+", deltaY: "+e),s.x+=t,s.y+=e,this._damage(s.x,s.y,s.w,s.h),this.flip())}viewportChangeSize(t,e){(!this._clipViewport||typeof t>"u"||typeof e>"u")&&(L("Setting viewport to full display region"),t=this._fbWidth,e=this._fbHeight),t=Math.floor(t),e=Math.floor(e),t>this._fbWidth&&(t=this._fbWidth),e>this._fbHeight&&(e=this._fbHeight);let s=this._viewportLoc;if(s.w!==t||s.h!==e){s.w=t,s.h=e;let r=this._target;r.width=t,r.height=e,this.viewportChangePos(0,0),this._damage(s.x,s.y,s.w,s.h),this.flip(),this._rescale(this._scale)}}absX(t){return this._scale===0?0:it(t/this._scale+this._viewportLoc.x)}absY(t){return this._scale===0?0:it(t/this._scale+this._viewportLoc.y)}resize(t,e){this._prevDrawStyle="",this._fbWidth=t,this._fbHeight=e;let s=this._backbuffer;if(s.width!==t||s.height!==e){let n=null;s.width>0&&s.height>0&&(n=this._drawCtx.getImageData(0,0,s.width,s.height)),s.width!==t&&(s.width=t),s.height!==e&&(s.height=e),n&&this._drawCtx.putImageData(n,0,0)}let r=this._viewportLoc;this.viewportChangeSize(r.w,r.h),this.viewportChangePos(0,0)}getImageData(){return this._drawCtx.getImageData(0,0,this.width,this.height)}toDataURL(t,e){return this._backbuffer.toDataURL(t,e)}toBlob(t,e,s){return this._backbuffer.toBlob(t,e,s)}_damage(t,e,s,r){tthis._damageBounds.right&&(this._damageBounds.right=t+s),e+r>this._damageBounds.bottom&&(this._damageBounds.bottom=e+r)}flip(t){if(this._renderQ.length!==0&&!t)this._renderQPush({type:"flip"});else{let e=this._damageBounds.left,s=this._damageBounds.top,r=this._damageBounds.right-e,n=this._damageBounds.bottom-s,a=e-this._viewportLoc.x,h=s-this._viewportLoc.y;a<0&&(r+=a,e-=a,a=0),h<0&&(n+=h,s-=h,h=0),a+r>this._viewportLoc.w&&(r=this._viewportLoc.w-a),h+n>this._viewportLoc.h&&(n=this._viewportLoc.h-h),r>0&&n>0&&this._targetCtx.drawImage(this._backbuffer,e,s,r,n,a,h,r,n),this._damageBounds.left=this._damageBounds.top=65535,this._damageBounds.right=this._damageBounds.bottom=0}}pending(){return this._renderQ.length>0}flush(){return this._renderQ.length===0?Promise.resolve():(this._flushPromise===null&&(this._flushPromise=new Promise(t=>{this._flushResolve=t})),this._flushPromise)}fillRect(t,e,s,r,n,a){this._renderQ.length!==0&&!a?this._renderQPush({type:"fill",x:t,y:e,width:s,height:r,color:n}):(this._setFillColor(n),this._drawCtx.fillRect(t,e,s,r),this._damage(t,e,s,r))}copyImage(t,e,s,r,n,a,h){this._renderQ.length!==0&&!h?this._renderQPush({type:"copy",oldX:t,oldY:e,x:s,y:r,width:n,height:a}):(this._drawCtx.mozImageSmoothingEnabled=!1,this._drawCtx.webkitImageSmoothingEnabled=!1,this._drawCtx.msImageSmoothingEnabled=!1,this._drawCtx.imageSmoothingEnabled=!1,this._drawCtx.drawImage(this._backbuffer,t,e,n,a,s,r,n,a),this._damage(s,r,n,a))}imageRect(t,e,s,r,n,a){if(s===0||r===0)return;let h=new Image;h.src="data: "+n+";base64,"+Pe.encode(a),this._renderQPush({type:"img",img:h,x:t,y:e,width:s,height:r})}videoFrame(t,e,s,r,n){this._renderQPush({type:"frame",frame:n,x:t,y:e,width:s,height:r})}blitImage(t,e,s,r,n,a,h){if(this._renderQ.length!==0&&!h){let _=new Uint8Array(s*r*4);_.set(new Uint8Array(n.buffer,0,_.length)),this._renderQPush({type:"blit",data:_,x:t,y:e,width:s,height:r})}else{let _=new Uint8ClampedArray(n.buffer,n.byteOffset+a,s*r*4),o=new ImageData(_,s,r);this._drawCtx.putImageData(o,t,e),this._damage(t,e,s,r)}}drawImage(t,...e){if(this._drawCtx.drawImage(t,...e),e.length<=4){let[s,r]=e;this._damage(s,r,t.width,t.height)}else{let[,,s,r,n,a]=e;this._damage(n,a,s,r)}}autoscale(t,e){let s;if(t===0||e===0)s=0;else{let r=this._viewportLoc,n=t/e;r.w/r.h>=n?s=t/r.w:s=e/r.h}this._rescale(s)}_rescale(t){this._scale=t;let e=this._viewportLoc,s=t*e.w+"px",r=t*e.h+"px";(this._target.style.width!==s||this._target.style.height!==r)&&(this._target.style.width=s,this._target.style.height=r)}_setFillColor(t){let e="rgb("+t[0]+","+t[1]+","+t[2]+")";e!==this._prevDrawStyle&&(this._drawCtx.fillStyle=e,this._prevDrawStyle=e)}_renderQPush(t){this._renderQ.push(t),this._renderQ.length===1&&this._scanRenderQ()}_resumeRenderQ(){this.removeEventListener("load",this._noVNCDisplay._resumeRenderQ),this._noVNCDisplay._scanRenderQ()}_scanRenderQ(){let t=!0;for(;t&&this._renderQ.length>0;){let e=this._renderQ[0];switch(e.type){case"flip":this.flip(!0);break;case"copy":this.copyImage(e.oldX,e.oldY,e.x,e.y,e.width,e.height,!0);break;case"fill":this.fillRect(e.x,e.y,e.width,e.height,e.color,!0);break;case"blit":this.blitImage(e.x,e.y,e.width,e.height,e.data,0,!0);break;case"img":if(e.img.complete){if(e.img.width!==e.width||e.img.height!==e.height){U("Decoded image has incorrect dimensions. Got "+e.img.width+"x"+e.img.height+". Expected "+e.width+"x"+e.height+".");return}this.drawImage(e.img,e.x,e.y)}else e.img._noVNCDisplay=this,e.img.addEventListener("load",this._resumeRenderQ),t=!1;break;case"frame":if(e.frame.ready){let s=e.frame.frame;(s.codedWidth{s._scanRenderQ()}),t=!1}break}t&&this._renderQ.shift()}this._renderQ.length===0&&this._flushPromise!==null&&(this._flushResolve(),this._flushPromise=null,this._flushResolve=null)}};function ae(i,t,e,s,r){if(t.subarray&&i.subarray){i.set(t.subarray(e,e+s),r);return}for(var n=0;n>>16&65535|0,a=0;e!==0;){a=e>2e3?2e3:e,e-=a;do r=r+t[s++]|0,n=n+r|0;while(--a);r%=65521,n%=65521}return r|n<<16|0}function G(){for(var i,t=[],e=0;e<256;e++){i=e;for(var s=0;s<8;s++)i=i&1?3988292384^i>>>1:i>>>1;t[e]=i}return t}var Ma=G();var Qt=30,qr=12;function di(i,t){var e,s,r,n,a,h,_,o,l,x,u,d,X,F,A,k,T,E,K,C,I,S,D,B,M;e=i.state,s=i.next_in,B=i.input,r=s+(i.avail_in-5),n=i.next_out,M=i.output,a=n-(t-i.avail_out),h=n+(i.avail_out-257),_=e.dmax,o=e.wsize,l=e.whave,x=e.wnext,u=e.window,d=e.hold,X=e.bits,F=e.lencode,A=e.distcode,k=(1<>>24,d>>>=K,X-=K,K=E>>>16&255,K===0)M[n++]=E&65535;else if(K&16){C=E&65535,K&=15,K&&(X>>=K,X-=K),X<15&&(d+=B[s++]<>>24,d>>>=K,X-=K,K=E>>>16&255,K&16){if(I=E&65535,K&=15,X_){i.msg="invalid distance too far back",e.mode=Qt;break e}if(d>>>=K,X-=K,K=n-a,I>K){if(K=I-K,K>l&&e.sane){i.msg="invalid distance too far back",e.mode=Qt;break e}if(S=0,D=u,x===0){if(S+=o-K,K2;)M[n++]=D[S++],M[n++]=D[S++],M[n++]=D[S++],C-=3;C&&(M[n++]=D[S++],C>1&&(M[n++]=D[S++]))}else{S=n-I;do M[n++]=M[S++],M[n++]=M[S++],M[n++]=M[S++],C-=3;while(C>2);C&&(M[n++]=M[S++],C>1&&(M[n++]=M[S++]))}}else if(K&64){i.msg="invalid distance code",e.mode=Qt;break e}else{E=A[(E&65535)+(d&(1<>3,s-=C,X-=C<<3,d&=(1<=1&&z[u]===0;u--);if(d>u&&(d=u),u===0)return r[n++]=1<<24|64<<16|0,r[n++]=1<<24|64<<16|0,h.bits=1,0;for(x=1;x0&&(i===hs||u!==1))return-1;for(te[1]=0,o=1;oas||i===ls&&k>os)return 1;for(;;){Fe=o-F,a[l]M?(_e=Ie[oe+a[l]],Z=D[B+a[l]]):(_e=96,Z=0),E=1<>F)+K]=Fe<<24|_e<<16|Z|0;while(K!==0);for(E=1<>=1;if(E!==0?(T&=E-1,T+=E):T=0,l++,--z[o]===0){if(o===u)break;o=t[e+a[l]]}if(o>d&&(T&I)!==C){for(F===0&&(F=d),S+=x,X=o-F,A=1<as||i===ls&&k>os)return 1;C=T&I,r[C]=d<<24|X<<16|S-n|0}}return T!==0&&(r[S+T]=o-F<<24|64<<16|0),h.bits=d,0}var sn=0,Is=1,Ps=2,cs=4,rn=5,It=6,Ut=0,nn=1,an=2,Ce=-2,on=-3,_s=-4,hn=-5,fs=8,Hs=1,us=2,ds=3,xs=4,ps=5,gs=6,bs=7,ms=8,ws=9,vs=10,ys=11,pe=12,pi=13,Xs=14,gi=15,Ks=16,Fs=17,ks=18,Ss=19,Pt=20,Ht=21,Es=22,As=23,Cs=24,Ls=25,Ts=26,bi=27,Ms=28,Rs=29,N=30,Bs=31,ln=32,cn=852,_n=592,fn=15,un=fn;function Ds(i){return(i>>>24&255)+(i>>>8&65280)+((i&65280)<<8)+((i&255)<<24)}function dn(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new ie(320),this.work=new ie(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function xn(i){var t;return!i||!i.state?Ce:(t=i.state,i.total_in=i.total_out=t.total=0,i.msg="",t.wrap&&(i.adler=t.wrap&1),t.mode=Hs,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new nt(cn),t.distcode=t.distdyn=new nt(_n),t.sane=1,t.back=-1,Ut)}function vi(i){var t;return!i||!i.state?Ce:(t=i.state,t.wsize=0,t.whave=0,t.wnext=0,xn(i))}function pn(i,t){var e,s;return!i||!i.state||(s=i.state,t<0?(e=0,t=-t):(e=(t>>4)+1,t<48&&(t&=15)),t&&(t<8||t>15))?Ce:(s.window!==null&&s.wbits!==t&&(s.window=null),s.wrap=e,s.wbits=t,vi(i))}function gn(i,t){var e,s;return i?(s=new dn,i.state=s,s.window=null,e=pn(i,t),e!==Ut&&(i.state=null),e):Ce}function Us(i){return gn(i,un)}var Qs=!0,mi,wi;function bn(i){if(Qs){var t;for(mi=new nt(512),wi=new nt(32),t=0;t<144;)i.lens[t++]=8;for(;t<256;)i.lens[t++]=9;for(;t<280;)i.lens[t++]=7;for(;t<288;)i.lens[t++]=8;for(Ae(Is,i.lens,0,288,mi,0,i.work,{bits:9}),t=0;t<32;)i.lens[t++]=5;Ae(Ps,i.lens,0,32,wi,0,i.work,{bits:5}),Qs=!1}i.lencode=mi,i.lenbits=9,i.distcode=wi,i.distbits=5}function mn(i,t,e,s){var r,n=i.state;return n.window===null&&(n.wsize=1<=n.wsize?(ae(n.window,t,e-n.wsize,n.wsize,0),n.wnext=0,n.whave=n.wsize):(r=n.wsize-n.wnext,r>s&&(r=s),ae(n.window,t,e-s,r,n.wnext),s-=r,s?(ae(n.window,t,e-s,s,0),n.wnext=s,n.whave=n.wsize):(n.wnext+=r,n.wnext===n.wsize&&(n.wnext=0),n.whave>>8&255,e.check=G(e.check,B,2,0),o=0,l=0,e.mode=us;break}if(e.flags=0,e.head&&(e.head.done=!1),!(e.wrap&1)||(((o&255)<<8)+(o>>8))%31){i.msg="incorrect header check",e.mode=N;break}if((o&15)!==fs){i.msg="unknown compression method",e.mode=N;break}if(o>>>=4,l-=4,S=(o&15)+8,e.wbits===0)e.wbits=S;else if(S>e.wbits){i.msg="invalid window size",e.mode=N;break}e.dmax=1<>8&1),e.flags&512&&(B[0]=o&255,B[1]=o>>>8&255,e.check=G(e.check,B,2,0)),o=0,l=0,e.mode=ds;case ds:for(;l<32;){if(h===0)break e;h--,o+=s[n++]<>>8&255,B[2]=o>>>16&255,B[3]=o>>>24&255,e.check=G(e.check,B,4,0)),o=0,l=0,e.mode=xs;case xs:for(;l<16;){if(h===0)break e;h--,o+=s[n++]<>8),e.flags&512&&(B[0]=o&255,B[1]=o>>>8&255,e.check=G(e.check,B,2,0)),o=0,l=0,e.mode=ps;case ps:if(e.flags&1024){for(;l<16;){if(h===0)break e;h--,o+=s[n++]<>>8&255,e.check=G(e.check,B,2,0)),o=0,l=0}else e.head&&(e.head.extra=null);e.mode=gs;case gs:if(e.flags&1024&&(d=e.length,d>h&&(d=h),d&&(e.head&&(S=e.head.extra_len-e.length,e.head.extra||(e.head.extra=new Array(e.head.extra_len)),ae(e.head.extra,s,n,d,S)),e.flags&512&&(e.check=G(e.check,s,d,n)),h-=d,n+=d,e.length-=d),e.length))break e;e.length=0,e.mode=bs;case bs:if(e.flags&2048){if(h===0)break e;d=0;do S=s[n+d++],e.head&&S&&e.length<65536&&(e.head.name+=String.fromCharCode(S));while(S&&d>9&1,e.head.done=!0),i.adler=e.check=0,e.mode=pe;break;case vs:for(;l<32;){if(h===0)break e;h--,o+=s[n++]<>>=l&7,l-=l&7,e.mode=bi;break}for(;l<3;){if(h===0)break e;h--,o+=s[n++]<>>=1,l-=1,o&3){case 0:e.mode=Xs;break;case 1:if(bn(e),e.mode=Pt,t===It){o>>>=2,l-=2;break e}break;case 2:e.mode=Fs;break;case 3:i.msg="invalid block type",e.mode=N}o>>>=2,l-=2;break;case Xs:for(o>>>=l&7,l-=l&7;l<32;){if(h===0)break e;h--,o+=s[n++]<>>16^65535)){i.msg="invalid stored block lengths",e.mode=N;break}if(e.length=o&65535,o=0,l=0,e.mode=gi,t===It)break e;case gi:e.mode=Ks;case Ks:if(d=e.length,d){if(d>h&&(d=h),d>_&&(d=_),d===0)break e;ae(r,s,n,d,a),h-=d,n+=d,_-=d,a+=d,e.length-=d;break}e.mode=pe;break;case Fs:for(;l<14;){if(h===0)break e;h--,o+=s[n++]<>>=5,l-=5,e.ndist=(o&31)+1,o>>>=5,l-=5,e.ncode=(o&15)+4,o>>>=4,l-=4,e.nlen>286||e.ndist>30){i.msg="too many length or distance symbols",e.mode=N;break}e.have=0,e.mode=ks;case ks:for(;e.have>>=3,l-=3}for(;e.have<19;)e.lens[te[e.have++]]=0;if(e.lencode=e.lendyn,e.lenbits=7,M={bits:e.lenbits},D=Ae(sn,e.lens,0,19,e.lencode,0,e.work,M),e.lenbits=M.bits,D){i.msg="invalid code lengths set",e.mode=N;break}e.have=0,e.mode=Ss;case Ss:for(;e.have>>24,T=A>>>16&255,E=A&65535,!(k<=l);){if(h===0)break e;h--,o+=s[n++]<>>=k,l-=k,e.lens[e.have++]=E;else{if(E===16){for(z=k+2;l>>=k,l-=k,e.have===0){i.msg="invalid bit length repeat",e.mode=N;break}S=e.lens[e.have-1],d=3+(o&3),o>>>=2,l-=2}else if(E===17){for(z=k+3;l>>=k,l-=k,S=0,d=3+(o&7),o>>>=3,l-=3}else{for(z=k+7;l>>=k,l-=k,S=0,d=11+(o&127),o>>>=7,l-=7}if(e.have+d>e.nlen+e.ndist){i.msg="invalid bit length repeat",e.mode=N;break}for(;d--;)e.lens[e.have++]=S}}if(e.mode===N)break;if(e.lens[256]===0){i.msg="invalid code -- missing end-of-block",e.mode=N;break}if(e.lenbits=9,M={bits:e.lenbits},D=Ae(Is,e.lens,0,e.nlen,e.lencode,0,e.work,M),e.lenbits=M.bits,D){i.msg="invalid literal/lengths set",e.mode=N;break}if(e.distbits=6,e.distcode=e.distdyn,M={bits:e.distbits},D=Ae(Ps,e.lens,e.nlen,e.ndist,e.distcode,0,e.work,M),e.distbits=M.bits,D){i.msg="invalid distances set",e.mode=N;break}if(e.mode=Pt,t===It)break e;case Pt:e.mode=Ht;case Ht:if(h>=6&&_>=258){i.next_out=a,i.avail_out=_,i.next_in=n,i.avail_in=h,e.hold=o,e.bits=l,di(i,u),a=i.next_out,r=i.output,_=i.avail_out,n=i.next_in,s=i.input,h=i.avail_in,o=e.hold,l=e.bits,e.mode===pe&&(e.back=-1);break}for(e.back=0;A=e.lencode[o&(1<>>24,T=A>>>16&255,E=A&65535,!(k<=l);){if(h===0)break e;h--,o+=s[n++]<>K)],k=A>>>24,T=A>>>16&255,E=A&65535,!(K+k<=l);){if(h===0)break e;h--,o+=s[n++]<>>=K,l-=K,e.back+=K}if(o>>>=k,l-=k,e.back+=k,e.length=E,T===0){e.mode=Ts;break}if(T&32){e.back=-1,e.mode=pe;break}if(T&64){i.msg="invalid literal/length code",e.mode=N;break}e.extra=T&15,e.mode=Es;case Es:if(e.extra){for(z=e.extra;l>>=e.extra,l-=e.extra,e.back+=e.extra}e.was=e.length,e.mode=As;case As:for(;A=e.distcode[o&(1<>>24,T=A>>>16&255,E=A&65535,!(k<=l);){if(h===0)break e;h--,o+=s[n++]<>K)],k=A>>>24,T=A>>>16&255,E=A&65535,!(K+k<=l);){if(h===0)break e;h--,o+=s[n++]<>>=K,l-=K,e.back+=K}if(o>>>=k,l-=k,e.back+=k,T&64){i.msg="invalid distance code",e.mode=N;break}e.offset=E,e.extra=T&15,e.mode=Cs;case Cs:if(e.extra){for(z=e.extra;l>>=e.extra,l-=e.extra,e.back+=e.extra}if(e.offset>e.dmax){i.msg="invalid distance too far back",e.mode=N;break}e.mode=Ls;case Ls:if(_===0)break e;if(d=u-_,e.offset>d){if(d=e.offset-d,d>e.whave&&e.sane){i.msg="invalid distance too far back",e.mode=N;break}d>e.wnext?(d-=e.wnext,X=e.wsize-d):X=e.wnext-d,d>e.length&&(d=e.length),F=e.window}else F=r,X=a-e.offset,d=e.length;d>_&&(d=_),_-=d,e.length-=d;do r[a++]=F[X++];while(--d);e.length===0&&(e.mode=Ht);break;case Ts:if(_===0)break e;r[a++]=e.length,_--,e.mode=Ht;break;case bi:if(e.wrap){for(;l<32;){if(h===0)break e;h--,o|=s[n++]<this.chunkSize&&(this.chunkSize=t,this.strm.output=new Uint8Array(this.chunkSize)),this.strm.next_out=0,this.strm.avail_out=t,Ns(this.strm,0)<0)throw new Error("zlib inflate failed");if(this.strm.next_out!=t)throw new Error("Incomplete zlib block");return new Uint8Array(this.strm.output.buffer,0,this.strm.next_out)}reset(){vi(this.strm)}};var wn=4,Os=0,zs=1,vn=2;function We(i){for(var t=i.length;--t>=0;)i[t]=0}var yn=0,js=1,Xn=2,Kn=3,Fn=258,Ei=29,ft=256,ht=ft+1+Ei,Ve=30,Ai=19,qs=2*ht+1,Le=15,yi=16,kn=7,Ci=256,Js=16,$s=17,er=18,ki=[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],Nt=[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,13],Sn=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],tr=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],En=512,ge=new Array((ht+2)*2);We(ge);var ot=new Array(Ve*2);We(ot);var lt=new Array(En);We(lt);var ct=new Array(Fn-Kn+1);We(ct);var Li=new Array(Ei);We(Li);var Ot=new Array(Ve);We(Ot);function Xi(i,t,e,s,r){this.static_tree=i,this.extra_bits=t,this.extra_base=e,this.elems=s,this.max_length=r,this.has_stree=i&&i.length}var ir,sr,rr;function Ki(i,t){this.dyn_tree=i,this.max_code=0,this.stat_desc=t}function nr(i){return i<256?lt[i]:lt[256+(i>>>7)]}function _t(i,t){i.pending_buf[i.pending++]=t&255,i.pending_buf[i.pending++]=t>>>8&255}function re(i,t,e){i.bi_valid>yi-e?(i.bi_buf|=t<>yi-i.bi_valid,i.bi_valid+=e-yi):(i.bi_buf|=t<>>=1,e<<=1;while(--t>0);return e>>>1}function An(i){i.bi_valid===16?(_t(i,i.bi_buf),i.bi_buf=0,i.bi_valid=0):i.bi_valid>=8&&(i.pending_buf[i.pending++]=i.bi_buf&255,i.bi_buf>>=8,i.bi_valid-=8)}function Cn(i,t){var e=t.dyn_tree,s=t.max_code,r=t.stat_desc.static_tree,n=t.stat_desc.has_stree,a=t.stat_desc.extra_bits,h=t.stat_desc.extra_base,_=t.stat_desc.max_length,o,l,x,u,d,X,F=0;for(u=0;u<=Le;u++)i.bl_count[u]=0;for(e[i.heap[i.heap_max]*2+1]=0,o=i.heap_max+1;o_&&(u=_,F++),e[l*2+1]=u,!(l>s)&&(i.bl_count[u]++,d=0,l>=h&&(d=a[l-h]),X=e[l*2],i.opt_len+=X*(u+d),n&&(i.static_len+=X*(r[l*2+1]+d)));if(F!==0){do{for(u=_-1;i.bl_count[u]===0;)u--;i.bl_count[u]--,i.bl_count[u+1]+=2,i.bl_count[_]--,F-=2}while(F>0);for(u=_;u!==0;u--)for(l=i.bl_count[u];l!==0;)x=i.heap[--o],!(x>s)&&(e[x*2+1]!==u&&(i.opt_len+=(u-e[x*2+1])*e[x*2],e[x*2+1]=u),l--)}}function or(i,t,e){var s=new Array(Le+1),r=0,n,a;for(n=1;n<=Le;n++)s[n]=r=r+e[n-1]<<1;for(a=0;a<=t;a++){var h=i[a*2+1];h!==0&&(i[a*2]=ar(s[h]++,h))}}function Ln(){var i,t,e,s,r,n=new Array(Le+1);for(e=0,s=0;s>=7;s8?_t(i,i.bi_buf):i.bi_valid>0&&(i.pending_buf[i.pending++]=i.bi_buf),i.bi_buf=0,i.bi_valid=0}function Tn(i,t,e,s){lr(i),s&&(_t(i,e),_t(i,~e)),ae(i.pending_buf,i.window,t,e,i.pending),i.pending+=e}function Gs(i,t,e,s){var r=t*2,n=e*2;return i[r]>1;a>=1;a--)Fi(i,e,a);o=n;do a=i.heap[1],i.heap[1]=i.heap[i.heap_len--],Fi(i,e,1),h=i.heap[1],i.heap[--i.heap_max]=a,i.heap[--i.heap_max]=h,e[o*2]=e[a*2]+e[h*2],i.depth[o]=(i.depth[a]>=i.depth[h]?i.depth[a]:i.depth[h])+1,e[a*2+1]=e[h*2+1]=o,i.heap[1]=o++,Fi(i,e,1);while(i.heap_len>=2);i.heap[--i.heap_max]=i.heap[1],Cn(i,t),or(e,_,i.bl_count)}function Ws(i,t,e){var s,r=-1,n,a=t[0*2+1],h=0,_=7,o=4;for(a===0&&(_=138,o=3),t[(e+1)*2+1]=65535,s=0;s<=e;s++)n=a,a=t[(s+1)*2+1],!(++h<_&&n===a)&&(h=3&&i.bl_tree[tr[t]*2+1]===0;t--);return i.opt_len+=3*(t+1)+5+5+4,t}function Rn(i,t,e,s){var r;for(re(i,t-257,5),re(i,e-1,5),re(i,s-4,4),r=0;r>>=1)if(t&1&&i.dyn_ltree[e*2]!==0)return Os;if(i.dyn_ltree[9*2]!==0||i.dyn_ltree[10*2]!==0||i.dyn_ltree[13*2]!==0)return zs;for(e=32;e0?(i.strm.data_type===vn&&(i.strm.data_type=Bn(i)),Si(i,i.l_desc),Si(i,i.d_desc),a=Mn(i),r=i.opt_len+3+7>>>3,n=i.static_len+3+7>>>3,n<=r&&(r=n)):r=n=e+5,e+4<=r&&t!==-1?Ti(i,t,e,s):i.strategy===wn||n===r?(re(i,(js<<1)+(s?1:0),3),Vs(i,ge,ot)):(re(i,(Xn<<1)+(s?1:0),3),Rn(i,i.l_desc.max_code+1,i.d_desc.max_code+1,a+1),Vs(i,i.dyn_ltree,i.dyn_dtree)),hr(i),s&&lr(i)}function be(i,t,e){return i.pending_buf[i.d_buf+i.last_lit*2]=t>>>8&255,i.pending_buf[i.d_buf+i.last_lit*2+1]=t&255,i.pending_buf[i.l_buf+i.last_lit]=e&255,i.last_lit++,t===0?i.dyn_ltree[e*2]++:(i.matches++,t--,i.dyn_ltree[(ct[e]+ft+1)*2]++,i.dyn_dtree[nr(t)*2]++),i.last_lit===i.lit_bufsize-1}var ur={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"};var Be=0,Qn=1,Vt=3,ye=4,dr=5,Te=0,xr=1,qe=-2;var Mi=-5,Pi=-1,In=1,zt=2,Pn=3,Hn=4,Un=0,Nn=2,Wt=8,On=9,zn=15,Gn=8,Vn=29,Wn=256,Ii=Wn+1+Vn,Zn=30,Yn=19,jn=2*Ii+1,qn=15,P=3,ve=258,ce=ve+P+1,Jn=32,br=42,pr=69,Ri=73,Bi=91,Di=103,dt=113,Gt=666,Y=1,xt=2,Re=3,Je=4,$n=3;function Ye(i,t){return i.msg=ur[t],t}function gr(i){return(i<<1)-(i>4?9:0)}function Me(i){for(var t=i.length;--t>=0;)i[t]=0}function we(i){var t=i.state,e=t.pending;e>i.avail_out&&(e=i.avail_out),e!==0&&(ae(i.output,t.pending_buf,t.pending_out,e,i.next_out),i.next_out+=e,t.pending_out+=e,i.total_out+=e,i.avail_out-=e,t.pending-=e,t.pending===0&&(t.pending_out=0))}function j(i,t){fr(i,i.block_start>=0?i.block_start:-1,i.strstart-i.block_start,t),i.block_start=i.strstart,we(i.strm)}function Q(i,t){i.pending_buf[i.pending++]=t}function ut(i,t){i.pending_buf[i.pending++]=t>>>8&255,i.pending_buf[i.pending++]=t&255}function ea(i,t,e,s){var r=i.avail_in;return r>s&&(r=s),r===0?0:(i.avail_in-=r,ae(t,i.input,i.next_in,r,e),i.state.wrap===1?i.adler=ze(i.adler,t,r,e):i.state.wrap===2&&(i.adler=G(i.adler,t,r,e)),i.next_in+=r,i.total_in+=r,r)}function mr(i,t){var e=i.max_chain_length,s=i.strstart,r,n,a=i.prev_length,h=i.nice_match,_=i.strstart>i.w_size-ce?i.strstart-(i.w_size-ce):0,o=i.window,l=i.w_mask,x=i.prev,u=i.strstart+ve,d=o[s+a-1],X=o[s+a];i.prev_length>=i.good_match&&(e>>=2),h>i.lookahead&&(h=i.lookahead);do if(r=t,!(o[r+a]!==X||o[r+a-1]!==d||o[r]!==o[s]||o[++r]!==o[s+1])){s+=2,r++;do;while(o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&o[++s]===o[++r]&&sa){if(i.match_start=t,a=n,n>=h)break;d=o[s+a-1],X=o[s+a]}}while((t=x[t&l])>_&&--e!==0);return a<=i.lookahead?a:i.lookahead}function pt(i){var t=i.w_size,e,s,r,n,a;do{if(n=i.window_size-i.lookahead-i.strstart,i.strstart>=t+(t-ce)){ae(i.window,i.window,t,t,0),i.match_start-=t,i.strstart-=t,i.block_start-=t,s=i.hash_size,e=s;do r=i.head[--e],i.head[e]=r>=t?r-t:0;while(--s);s=t,e=s;do r=i.prev[--e],i.prev[e]=r>=t?r-t:0;while(--s);n+=t}if(i.strm.avail_in===0)break;if(s=ea(i.strm,i.window,i.strstart+i.lookahead,n),i.lookahead+=s,i.lookahead+i.insert>=P)for(a=i.strstart-i.insert,i.ins_h=i.window[a],i.ins_h=(i.ins_h<i.pending_buf_size-5&&(e=i.pending_buf_size-5);;){if(i.lookahead<=1){if(pt(i),i.lookahead===0&&t===Be)return Y;if(i.lookahead===0)break}i.strstart+=i.lookahead,i.lookahead=0;var s=i.block_start+e;if((i.strstart===0||i.strstart>=s)&&(i.lookahead=i.strstart-s,i.strstart=s,j(i,!1),i.strm.avail_out===0)||i.strstart-i.block_start>=i.w_size-ce&&(j(i,!1),i.strm.avail_out===0))return Y}return i.insert=0,t===ye?(j(i,!0),i.strm.avail_out===0?Re:Je):(i.strstart>i.block_start&&(j(i,!1),i.strm.avail_out===0),Y)}function Qi(i,t){for(var e,s;;){if(i.lookahead=P&&(i.ins_h=(i.ins_h<=P)if(s=be(i,i.strstart-i.match_start,i.match_length-P),i.lookahead-=i.match_length,i.match_length<=i.max_lazy_match&&i.lookahead>=P){i.match_length--;do i.strstart++,i.ins_h=(i.ins_h<=P&&(i.ins_h=(i.ins_h<4096)&&(i.match_length=P-1)),i.prev_length>=P&&i.match_length<=i.prev_length){r=i.strstart+i.lookahead-P,s=be(i,i.strstart-1-i.prev_match,i.prev_length-P),i.lookahead-=i.prev_length-1,i.prev_length-=2;do++i.strstart<=r&&(i.ins_h=(i.ins_h<=P&&i.strstart>0&&(r=i.strstart-1,s=a[r],s===a[++r]&&s===a[++r]&&s===a[++r])){n=i.strstart+ve;do;while(s===a[++r]&&s===a[++r]&&s===a[++r]&&s===a[++r]&&s===a[++r]&&s===a[++r]&&s===a[++r]&&s===a[++r]&&ri.lookahead&&(i.match_length=i.lookahead)}if(i.match_length>=P?(e=be(i,1,i.match_length-P),i.lookahead-=i.match_length,i.strstart+=i.match_length,i.match_length=0):(e=be(i,0,i.window[i.strstart]),i.lookahead--,i.strstart++),e&&(j(i,!1),i.strm.avail_out===0))return Y}return i.insert=0,t===ye?(j(i,!0),i.strm.avail_out===0?Re:Je):i.last_lit&&(j(i,!1),i.strm.avail_out===0)?Y:xt}function sa(i,t){for(var e;;){if(i.lookahead===0&&(pt(i),i.lookahead===0)){if(t===Be)return Y;break}if(i.match_length=0,e=be(i,0,i.window[i.strstart]),i.lookahead--,i.strstart++,e&&(j(i,!1),i.strm.avail_out===0))return Y}return i.insert=0,t===ye?(j(i,!0),i.strm.avail_out===0?Re:Je):i.last_lit&&(j(i,!1),i.strm.avail_out===0)?Y:xt}function ue(i,t,e,s,r){this.good_length=i,this.max_lazy=t,this.nice_length=e,this.max_chain=s,this.func=r}var je;je=[new ue(0,0,0,0,ta),new ue(4,4,8,4,Qi),new ue(4,5,16,8,Qi),new ue(4,6,32,32,Qi),new ue(4,4,16,16,Ze),new ue(8,16,32,32,Ze),new ue(8,16,128,128,Ze),new ue(8,32,128,256,Ze),new ue(32,128,258,1024,Ze),new ue(32,258,258,4096,Ze)];function ra(i){i.window_size=2*i.w_size,Me(i.head),i.max_lazy_match=je[i.level].max_lazy,i.good_match=je[i.level].good_length,i.nice_match=je[i.level].nice_length,i.max_chain_length=je[i.level].max_chain,i.strstart=0,i.block_start=0,i.lookahead=0,i.insert=0,i.match_length=i.prev_length=P-1,i.match_available=0,i.ins_h=0}function na(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Wt,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new ie(jn*2),this.dyn_dtree=new ie((2*Zn+1)*2),this.bl_tree=new ie((2*Yn+1)*2),Me(this.dyn_ltree),Me(this.dyn_dtree),Me(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new ie(qn+1),this.heap=new ie(2*Ii+1),Me(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new ie(2*Ii+1),Me(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function aa(i){var t;return!i||!i.state?Ye(i,qe):(i.total_in=i.total_out=0,i.data_type=Nn,t=i.state,t.pending=0,t.pending_out=0,t.wrap<0&&(t.wrap=-t.wrap),t.status=t.wrap?br:dt,i.adler=t.wrap===2?0:1,t.last_flush=Be,cr(t),Te)}function oa(i){var t=aa(i);return t===Te&&ra(i.state),t}function ha(i,t,e,s,r,n){if(!i)return qe;var a=1;if(t===Pi&&(t=6),s<0?(a=0,s=-s):s>15&&(a=2,s-=16),r<1||r>On||e!==Wt||s<8||s>15||t<0||t>9||n<0||n>Hn)return Ye(i,qe);s===8&&(s=9);var h=new na;return i.state=h,h.strm=i,h.wrap=a,h.gzhead=null,h.w_bits=s,h.w_size=1<dr||t<0)return i?Ye(i,qe):qe;if(s=i.state,!i.output||!i.input&&i.avail_in!==0||s.status===Gt&&t!==ye)return Ye(i,i.avail_out===0?Mi:qe);if(s.strm=i,e=s.last_flush,s.last_flush=t,s.status===br)if(s.wrap===2)i.adler=0,Q(s,31),Q(s,139),Q(s,8),s.gzhead?(Q(s,(s.gzhead.text?1:0)+(s.gzhead.hcrc?2:0)+(s.gzhead.extra?4:0)+(s.gzhead.name?8:0)+(s.gzhead.comment?16:0)),Q(s,s.gzhead.time&255),Q(s,s.gzhead.time>>8&255),Q(s,s.gzhead.time>>16&255),Q(s,s.gzhead.time>>24&255),Q(s,s.level===9?2:s.strategy>=zt||s.level<2?4:0),Q(s,s.gzhead.os&255),s.gzhead.extra&&s.gzhead.extra.length&&(Q(s,s.gzhead.extra.length&255),Q(s,s.gzhead.extra.length>>8&255)),s.gzhead.hcrc&&(i.adler=G(i.adler,s.pending_buf,s.pending,0)),s.gzindex=0,s.status=pr):(Q(s,0),Q(s,0),Q(s,0),Q(s,0),Q(s,0),Q(s,s.level===9?2:s.strategy>=zt||s.level<2?4:0),Q(s,$n),s.status=dt);else{var a=Wt+(s.w_bits-8<<4)<<8,h=-1;s.strategy>=zt||s.level<2?h=0:s.level<6?h=1:s.level===6?h=2:h=3,a|=h<<6,s.strstart!==0&&(a|=Jn),a+=31-a%31,s.status=dt,ut(s,a),s.strstart!==0&&(ut(s,i.adler>>>16),ut(s,i.adler&65535)),i.adler=1}if(s.status===pr)if(s.gzhead.extra){for(r=s.pending;s.gzindex<(s.gzhead.extra.length&65535)&&!(s.pending===s.pending_buf_size&&(s.gzhead.hcrc&&s.pending>r&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),we(i),r=s.pending,s.pending===s.pending_buf_size));)Q(s,s.gzhead.extra[s.gzindex]&255),s.gzindex++;s.gzhead.hcrc&&s.pending>r&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),s.gzindex===s.gzhead.extra.length&&(s.gzindex=0,s.status=Ri)}else s.status=Ri;if(s.status===Ri)if(s.gzhead.name){r=s.pending;do{if(s.pending===s.pending_buf_size&&(s.gzhead.hcrc&&s.pending>r&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),we(i),r=s.pending,s.pending===s.pending_buf_size)){n=1;break}s.gzindexr&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),n===0&&(s.gzindex=0,s.status=Bi)}else s.status=Bi;if(s.status===Bi)if(s.gzhead.comment){r=s.pending;do{if(s.pending===s.pending_buf_size&&(s.gzhead.hcrc&&s.pending>r&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),we(i),r=s.pending,s.pending===s.pending_buf_size)){n=1;break}s.gzindexr&&(i.adler=G(i.adler,s.pending_buf,s.pending-r,r)),n===0&&(s.status=Di)}else s.status=Di;if(s.status===Di&&(s.gzhead.hcrc?(s.pending+2>s.pending_buf_size&&we(i),s.pending+2<=s.pending_buf_size&&(Q(s,i.adler&255),Q(s,i.adler>>8&255),i.adler=0,s.status=dt)):s.status=dt),s.pending!==0){if(we(i),i.avail_out===0)return s.last_flush=-1,Te}else if(i.avail_in===0&&gr(t)<=gr(e)&&t!==ye)return Ye(i,Mi);if(s.status===Gt&&i.avail_in!==0)return Ye(i,Mi);if(i.avail_in!==0||s.lookahead!==0||t!==Be&&s.status!==Gt){var _=s.strategy===zt?sa(s,t):s.strategy===Pn?ia(s,t):je[s.level].func(s,t);if((_===Re||_===Je)&&(s.status=Gt),_===Y||_===Re)return i.avail_out===0&&(s.last_flush=-1),Te;if(_===xt&&(t===Qn?_r(s):t!==dr&&(Ti(s,0,0,!1),t===Vt&&(Me(s.head),s.lookahead===0&&(s.strstart=0,s.block_start=0,s.insert=0))),we(i),i.avail_out===0))return s.last_flush=-1,Te}return t!==ye?Te:s.wrap<=0?xr:(s.wrap===2?(Q(s,i.adler&255),Q(s,i.adler>>8&255),Q(s,i.adler>>16&255),Q(s,i.adler>>24&255),Q(s,i.total_in&255),Q(s,i.total_in>>8&255),Q(s,i.total_in>>16&255),Q(s,i.total_in>>24&255)):(ut(s,i.adler>>>16),ut(s,i.adler&65535)),we(i),s.wrap>0&&(s.wrap=-s.wrap),s.pending!==0?Te:xr)}var gt=class{constructor(){this.strm=new at,this.chunkSize=1024*10*10,this.outputBuffer=new Uint8Array(this.chunkSize),wr(this.strm,Pi)}deflate(t){this.strm.input=t,this.strm.avail_in=this.strm.input.length,this.strm.next_in=0,this.strm.output=this.outputBuffer,this.strm.avail_out=this.chunkSize,this.strm.next_out=0;let e=Hi(this.strm,Vt),s=new Uint8Array(this.strm.output.buffer,0,this.strm.next_out);if(e<0)throw new Error("zlib deflate failed");if(this.strm.avail_in>0){let r=[s],n=s.length;do{if(this.strm.output=new Uint8Array(this.chunkSize),this.strm.next_out=0,this.strm.avail_out=this.chunkSize,e=Hi(this.strm,Vt),e<0)throw new Error("zlib deflate failed");let _=new Uint8Array(this.strm.output.buffer,0,this.strm.next_out);n+=_.length,r.push(_)}while(this.strm.avail_in>0);let a=new Uint8Array(n),h=0;for(let _=0;_=32&&i<=255)return i;let t=la[i];return t!==void 0?t:16777216|i}};var Ui={8:"Backspace",9:"Tab",10:"NumpadClear",13:"Enter",16:"ShiftLeft",17:"ControlLeft",18:"AltLeft",19:"Pause",20:"CapsLock",21:"Lang1",25:"Lang2",27:"Escape",28:"Convert",29:"NonConvert",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",41:"Select",44:"PrintScreen",45:"Insert",46:"Delete",47:"Help",48:"Digit0",49:"Digit1",50:"Digit2",51:"Digit3",52:"Digit4",53:"Digit5",54:"Digit6",55:"Digit7",56:"Digit8",57:"Digit9",91:"MetaLeft",92:"MetaRight",93:"ContextMenu",95:"Sleep",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9",106:"NumpadMultiply",107:"NumpadAdd",108:"NumpadDecimal",109:"NumpadSubtract",110:"NumpadDecimal",111:"NumpadDivide",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",124:"F13",125:"F14",126:"F15",127:"F16",128:"F17",129:"F18",130:"F19",131:"F20",132:"F21",133:"F22",134:"F23",135:"F24",144:"NumLock",145:"ScrollLock",166:"BrowserBack",167:"BrowserForward",168:"BrowserRefresh",169:"BrowserStop",170:"BrowserSearch",171:"BrowserFavorites",172:"BrowserHome",173:"AudioVolumeMute",174:"AudioVolumeDown",175:"AudioVolumeUp",176:"MediaTrackNext",177:"MediaTrackPrevious",178:"MediaStop",179:"MediaPlayPause",180:"LaunchMail",181:"MediaSelect",182:"LaunchApp1",183:"LaunchApp2",225:"AltRight"};var Ni={Backspace:"Backspace",AltLeft:"Alt",AltRight:"Alt",CapsLock:"CapsLock",ContextMenu:"ContextMenu",ControlLeft:"Control",ControlRight:"Control",Enter:"Enter",MetaLeft:"Meta",MetaRight:"Meta",ShiftLeft:"Shift",ShiftRight:"Shift",Tab:"Tab",Delete:"Delete",End:"End",Help:"Help",Home:"Home",Insert:"Insert",PageDown:"PageDown",PageUp:"PageUp",ArrowDown:"ArrowDown",ArrowLeft:"ArrowLeft",ArrowRight:"ArrowRight",ArrowUp:"ArrowUp",NumLock:"NumLock",NumpadBackspace:"Backspace",NumpadClear:"Clear",Escape:"Escape",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12",F13:"F13",F14:"F14",F15:"F15",F16:"F16",F17:"F17",F18:"F18",F19:"F19",F20:"F20",F21:"F21",F22:"F22",F23:"F23",F24:"F24",F25:"F25",F26:"F26",F27:"F27",F28:"F28",F29:"F29",F30:"F30",F31:"F31",F32:"F32",F33:"F33",F34:"F34",F35:"F35",PrintScreen:"PrintScreen",ScrollLock:"ScrollLock",Pause:"Pause",BrowserBack:"BrowserBack",BrowserFavorites:"BrowserFavorites",BrowserForward:"BrowserForward",BrowserHome:"BrowserHome",BrowserRefresh:"BrowserRefresh",BrowserSearch:"BrowserSearch",BrowserStop:"BrowserStop",Eject:"Eject",LaunchApp1:"LaunchMyComputer",LaunchApp2:"LaunchCalendar",LaunchMail:"LaunchMail",MediaPlayPause:"MediaPlay",MediaStop:"MediaStop",MediaTrackNext:"MediaTrackNext",MediaTrackPrevious:"MediaTrackPrevious",Power:"Power",Sleep:"Sleep",AudioVolumeDown:"AudioVolumeDown",AudioVolumeMute:"AudioVolumeMute",AudioVolumeUp:"AudioVolumeUp",WakeUp:"WakeUp"};var De={};function w(i,t){if(t===void 0)throw new Error('Undefined keysym for key "'+i+'"');if(i in De)throw new Error('Duplicate entry for key "'+i+'"');De[i]=[t,t,t,t]}function Zt(i,t,e){if(t===void 0)throw new Error('Undefined keysym for key "'+i+'"');if(e===void 0)throw new Error('Undefined keysym for key "'+i+'"');if(i in De)throw new Error('Duplicate entry for key "'+i+'"');De[i]=[t,t,e,t]}function H(i,t,e){if(t===void 0)throw new Error('Undefined keysym for key "'+i+'"');if(e===void 0)throw new Error('Undefined keysym for key "'+i+'"');if(i in De)throw new Error('Duplicate entry for key "'+i+'"');De[i]=[t,t,t,e]}Zt("Alt",f.XK_Alt_L,f.XK_Alt_R);w("AltGraph",f.XK_ISO_Level3_Shift);w("CapsLock",f.XK_Caps_Lock);Zt("Control",f.XK_Control_L,f.XK_Control_R);Zt("Meta",f.XK_Super_L,f.XK_Super_R);w("NumLock",f.XK_Num_Lock);w("ScrollLock",f.XK_Scroll_Lock);Zt("Shift",f.XK_Shift_L,f.XK_Shift_R);H("Enter",f.XK_Return,f.XK_KP_Enter);w("Tab",f.XK_Tab);H(" ",f.XK_space,f.XK_KP_Space);H("ArrowDown",f.XK_Down,f.XK_KP_Down);H("ArrowLeft",f.XK_Left,f.XK_KP_Left);H("ArrowRight",f.XK_Right,f.XK_KP_Right);H("ArrowUp",f.XK_Up,f.XK_KP_Up);H("End",f.XK_End,f.XK_KP_End);H("Home",f.XK_Home,f.XK_KP_Home);H("PageDown",f.XK_Next,f.XK_KP_Next);H("PageUp",f.XK_Prior,f.XK_KP_Prior);w("Backspace",f.XK_BackSpace);H("Clear",f.XK_Clear,f.XK_KP_Begin);w("Copy",f.XF86XK_Copy);w("Cut",f.XF86XK_Cut);H("Delete",f.XK_Delete,f.XK_KP_Delete);H("Insert",f.XK_Insert,f.XK_KP_Insert);w("Paste",f.XF86XK_Paste);w("Redo",f.XK_Redo);w("Undo",f.XK_Undo);w("Cancel",f.XK_Cancel);w("ContextMenu",f.XK_Menu);w("Escape",f.XK_Escape);w("Execute",f.XK_Execute);w("Find",f.XK_Find);w("Help",f.XK_Help);w("Pause",f.XK_Pause);w("Select",f.XK_Select);w("ZoomIn",f.XF86XK_ZoomIn);w("ZoomOut",f.XF86XK_ZoomOut);w("BrightnessDown",f.XF86XK_MonBrightnessDown);w("BrightnessUp",f.XF86XK_MonBrightnessUp);w("Eject",f.XF86XK_Eject);w("LogOff",f.XF86XK_LogOff);w("Power",f.XF86XK_PowerOff);w("PowerOff",f.XF86XK_PowerDown);w("PrintScreen",f.XK_Print);w("Hibernate",f.XF86XK_Hibernate);w("Standby",f.XF86XK_Standby);w("WakeUp",f.XF86XK_WakeUp);w("AllCandidates",f.XK_MultipleCandidate);w("Alphanumeric",f.XK_Eisu_toggle);w("CodeInput",f.XK_Codeinput);w("Compose",f.XK_Multi_key);w("Convert",f.XK_Henkan);w("GroupFirst",f.XK_ISO_First_Group);w("GroupLast",f.XK_ISO_Last_Group);w("GroupNext",f.XK_ISO_Next_Group);w("GroupPrevious",f.XK_ISO_Prev_Group);w("NonConvert",f.XK_Muhenkan);w("PreviousCandidate",f.XK_PreviousCandidate);w("SingleCandidate",f.XK_SingleCandidate);w("HangulMode",f.XK_Hangul);w("HanjaMode",f.XK_Hangul_Hanja);w("JunjaMode",f.XK_Hangul_Jeonja);w("Eisu",f.XK_Eisu_toggle);w("Hankaku",f.XK_Hankaku);w("Hiragana",f.XK_Hiragana);w("HiraganaKatakana",f.XK_Hiragana_Katakana);w("KanaMode",f.XK_Kana_Shift);w("KanjiMode",f.XK_Kanji);w("Katakana",f.XK_Katakana);w("Romaji",f.XK_Romaji);w("Zenkaku",f.XK_Zenkaku);w("ZenkakuHankaku",f.XK_Zenkaku_Hankaku);w("F1",f.XK_F1);w("F2",f.XK_F2);w("F3",f.XK_F3);w("F4",f.XK_F4);w("F5",f.XK_F5);w("F6",f.XK_F6);w("F7",f.XK_F7);w("F8",f.XK_F8);w("F9",f.XK_F9);w("F10",f.XK_F10);w("F11",f.XK_F11);w("F12",f.XK_F12);w("F13",f.XK_F13);w("F14",f.XK_F14);w("F15",f.XK_F15);w("F16",f.XK_F16);w("F17",f.XK_F17);w("F18",f.XK_F18);w("F19",f.XK_F19);w("F20",f.XK_F20);w("F21",f.XK_F21);w("F22",f.XK_F22);w("F23",f.XK_F23);w("F24",f.XK_F24);w("F25",f.XK_F25);w("F26",f.XK_F26);w("F27",f.XK_F27);w("F28",f.XK_F28);w("F29",f.XK_F29);w("F30",f.XK_F30);w("F31",f.XK_F31);w("F32",f.XK_F32);w("F33",f.XK_F33);w("F34",f.XK_F34);w("F35",f.XK_F35);w("Close",f.XF86XK_Close);w("MailForward",f.XF86XK_MailForward);w("MailReply",f.XF86XK_Reply);w("MailSend",f.XF86XK_Send);w("MediaFastForward",f.XF86XK_AudioForward);w("MediaPause",f.XF86XK_AudioPause);w("MediaPlay",f.XF86XK_AudioPlay);w("MediaRecord",f.XF86XK_AudioRecord);w("MediaRewind",f.XF86XK_AudioRewind);w("MediaStop",f.XF86XK_AudioStop);w("MediaTrackNext",f.XF86XK_AudioNext);w("MediaTrackPrevious",f.XF86XK_AudioPrev);w("New",f.XF86XK_New);w("Open",f.XF86XK_Open);w("Print",f.XK_Print);w("Save",f.XF86XK_Save);w("SpellCheck",f.XF86XK_Spell);w("AudioVolumeDown",f.XF86XK_AudioLowerVolume);w("AudioVolumeUp",f.XF86XK_AudioRaiseVolume);w("AudioVolumeMute",f.XF86XK_AudioMute);w("MicrophoneVolumeMute",f.XF86XK_AudioMicMute);w("LaunchApplication1",f.XF86XK_MyComputer);w("LaunchApplication2",f.XF86XK_Calculator);w("LaunchCalendar",f.XF86XK_Calendar);w("LaunchMail",f.XF86XK_Mail);w("LaunchMediaPlayer",f.XF86XK_AudioMedia);w("LaunchMusicPlayer",f.XF86XK_Music);w("LaunchPhone",f.XF86XK_Phone);w("LaunchScreenSaver",f.XF86XK_ScreenSaver);w("LaunchSpreadsheet",f.XF86XK_Excel);w("LaunchWebBrowser",f.XF86XK_WWW);w("LaunchWebCam",f.XF86XK_WebCam);w("LaunchWordProcessor",f.XF86XK_Word);w("BrowserBack",f.XF86XK_Back);w("BrowserFavorites",f.XF86XK_Favorites);w("BrowserForward",f.XF86XK_Forward);w("BrowserHome",f.XF86XK_HomePage);w("BrowserRefresh",f.XF86XK_Refresh);w("BrowserSearch",f.XF86XK_Search);w("BrowserStop",f.XF86XK_Stop);w("Dimmer",f.XF86XK_BrightnessAdjust);w("MediaAudioTrack",f.XF86XK_AudioCycleTrack);w("RandomToggle",f.XF86XK_AudioRandomPlay);w("SplitScreenToggle",f.XF86XK_SplitScreen);w("Subtitle",f.XF86XK_Subtitle);w("VideoModeNext",f.XF86XK_Next_VMode);H("=",f.XK_equal,f.XK_KP_Equal);H("+",f.XK_plus,f.XK_KP_Add);H("-",f.XK_minus,f.XK_KP_Subtract);H("*",f.XK_asterisk,f.XK_KP_Multiply);H("/",f.XK_slash,f.XK_KP_Divide);H(".",f.XK_period,f.XK_KP_Decimal);H(",",f.XK_comma,f.XK_KP_Separator);H("0",f.XK_0,f.XK_KP_0);H("1",f.XK_1,f.XK_KP_1);H("2",f.XK_2,f.XK_KP_2);H("3",f.XK_3,f.XK_KP_3);H("4",f.XK_4,f.XK_KP_4);H("5",f.XK_5,f.XK_KP_5);H("6",f.XK_6,f.XK_KP_6);H("7",f.XK_7,f.XK_KP_7);H("8",f.XK_8,f.XK_KP_8);H("9",f.XK_9,f.XK_KP_9);var Oi=De;function $e(i){if(i.code){switch(i.code){case"OSLeft":return"MetaLeft";case"OSRight":return"MetaRight"}return i.code}if(i.keyCode in Ui){let t=Ui[i.keyCode];if(me()&&t==="ContextMenu"&&(t="MetaRight"),i.location===2)switch(t){case"ShiftLeft":return"ShiftRight";case"ControlLeft":return"ControlRight";case"AltLeft":return"AltRight"}if(i.location===3)switch(t){case"Delete":return"NumpadDecimal";case"Insert":return"Numpad0";case"End":return"Numpad1";case"ArrowDown":return"Numpad2";case"PageDown":return"Numpad3";case"ArrowLeft":return"Numpad4";case"ArrowRight":return"Numpad6";case"Home":return"Numpad7";case"ArrowUp":return"Numpad8";case"PageUp":return"Numpad9";case"Enter":return"NumpadEnter"}return t}return"Unidentified"}function ca(i){if(i.key!==void 0&&i.key!=="Unidentified"){switch(i.key){case"OS":return"Meta";case"LaunchMyComputer":return"LaunchApplication1";case"LaunchCalculator":return"LaunchApplication2"}switch(i.key){case"UIKeyInputUpArrow":return"ArrowUp";case"UIKeyInputDownArrow":return"ArrowDown";case"UIKeyInputLeftArrow":return"ArrowLeft";case"UIKeyInputRightArrow":return"ArrowRight";case"UIKeyInputEscape":return"Escape"}return i.key==="\0"&&i.code==="NumpadDecimal"?"Delete":i.key}let t=$e(i);return t in Ni?Ni[t]:i.charCode?String.fromCharCode(i.charCode):"Unidentified"}function yr(i){let t=ca(i);if(t==="Unidentified")return null;if(t in Oi){let s=i.location;if(t==="Meta"&&s===0&&(s=2),t==="Clear"&&s===3&&$e(i)==="NumLock"&&(s=0),(s===void 0||s>3)&&(s=0),t==="Meta"){let r=$e(i);if(r==="AltLeft")return f.XK_Meta_L;if(r==="AltRight")return f.XK_Meta_R}if(t==="Clear"&&$e(i)==="NumLock")return f.XK_Num_Lock;if(He())switch(t){case"Zenkaku":case"Hankaku":return f.XK_Zenkaku_Hankaku;case"Romaji":case"KanaMode":return f.XK_Romaji}return Oi[t][s]}if(t.length!==1)return null;let e=t.charCodeAt();return e?vr.lookup(e):null}var bt=class{constructor(t){this._target=t||null,this._keyDownList={},this._altGrArmed=!1,this._eventHandlers={keyup:this._handleKeyUp.bind(this),keydown:this._handleKeyDown.bind(this),blur:this._allKeysUp.bind(this)},this.onkeyevent=()=>{}}_sendKeyEvent(t,e,s,r=null,n=null){if(s)this._keyDownList[e]=t;else{if(!(e in this._keyDownList))return;delete this._keyDownList[e]}L("onkeyevent "+(s?"down":"up")+", keysym: "+t,", code: "+e+", numlock: "+r+", capslock: "+n),this.onkeyevent(t,e,s,r,n)}_getKeyCode(t){let e=$e(t);if(e!=="Unidentified")return e;if(t.keyCode&&t.keyCode!==229)return"Platform"+t.keyCode;if(t.keyIdentifier){if(t.keyIdentifier.substr(0,2)!=="U+")return t.keyIdentifier;let s=parseInt(t.keyIdentifier.substr(2),16);return"Platform"+String.fromCharCode(s).toUpperCase().charCodeAt()}return"Unidentified"}_handleKeyDown(t){let e=this._getKeyCode(t),s=yr(t),r=t.getModifierState("NumLock"),n=t.getModifierState("CapsLock");if((me()||Ue())&&(r=null),this._altGrArmed&&(this._altGrArmed=!1,clearTimeout(this._altGrTimeout),e==="AltRight"&&t.timeStamp-this._altGrCtrlTime<50?s=f.XK_ISO_Level3_Shift:this._sendKeyEvent(f.XK_Control_L,"ControlLeft",!0,r,n)),e==="Unidentified"){s&&(this._sendKeyEvent(s,e,!0,r,n),this._sendKeyEvent(s,e,!1,r,n)),Se(t);return}if(me()||Ue())switch(s){case f.XK_Super_L:s=f.XK_Alt_L;break;case f.XK_Super_R:s=f.XK_Super_L;break;case f.XK_Alt_L:s=f.XK_Mode_switch;break;case f.XK_Alt_R:s=f.XK_ISO_Level3_Shift;break}if(e in this._keyDownList&&(s=this._keyDownList[e]),(me()||Ue())&&t.metaKey&&e!=="MetaLeft"&&e!=="MetaRight"){this._sendKeyEvent(s,e,!0,r,n),this._sendKeyEvent(s,e,!1,r,n),Se(t);return}if((me()||Ue())&&e==="CapsLock"){this._sendKeyEvent(f.XK_Caps_Lock,"CapsLock",!0,r,n),this._sendKeyEvent(f.XK_Caps_Lock,"CapsLock",!1,r,n),Se(t);return}let a=[f.XK_Zenkaku_Hankaku,f.XK_Eisu_toggle,f.XK_Katakana,f.XK_Hiragana,f.XK_Romaji];if(He()&&a.includes(s)){this._sendKeyEvent(s,e,!0,r,n),this._sendKeyEvent(s,e,!1,r,n),Se(t);return}if(Se(t),e==="ControlLeft"&&He()&&!("ControlLeft"in this._keyDownList)){this._altGrArmed=!0,this._altGrTimeout=setTimeout(this._interruptAltGrSequence.bind(this),100),this._altGrCtrlTime=t.timeStamp;return}this._sendKeyEvent(s,e,!0,r,n)}_handleKeyUp(t){Se(t);let e=this._getKeyCode(t);if(this._interruptAltGrSequence(),(me()||Ue())&&e==="CapsLock"){this._sendKeyEvent(f.XK_Caps_Lock,"CapsLock",!0),this._sendKeyEvent(f.XK_Caps_Lock,"CapsLock",!1);return}this._sendKeyEvent(this._keyDownList[e],e,!1),He()&&(e==="ShiftLeft"||e==="ShiftRight")&&("ShiftRight"in this._keyDownList&&this._sendKeyEvent(this._keyDownList.ShiftRight,"ShiftRight",!1),"ShiftLeft"in this._keyDownList&&this._sendKeyEvent(this._keyDownList.ShiftLeft,"ShiftLeft",!1))}_interruptAltGrSequence(){this._altGrArmed&&(this._altGrArmed=!1,clearTimeout(this._altGrTimeout),this._sendKeyEvent(f.XK_Control_L,"ControlLeft",!0))}_allKeysUp(){L(">> Keyboard.allKeysUp"),this._interruptAltGrSequence();for(let t in this._keyDownList)this._sendKeyEvent(this._keyDownList[t],t,!1);L("<< Keyboard.allKeysUp")}grab(){this._target.addEventListener("keydown",this._eventHandlers.keydown),this._target.addEventListener("keyup",this._eventHandlers.keyup),window.addEventListener("blur",this._eventHandlers.blur)}ungrab(){this._target.removeEventListener("keydown",this._eventHandlers.keydown),this._target.removeEventListener("keyup",this._eventHandlers.keyup),window.removeEventListener("blur",this._eventHandlers.blur),this._allKeysUp()}};var mt=class{constructor(){this._target=null,this._state=127,this._tracked=[],this._ignored=[],this._waitingRelease=!1,this._releaseStart=0,this._longpressTimeoutId=null,this._twoTouchTimeoutId=null,this._boundEventHandler=this._eventHandler.bind(this)}attach(t){this.detach(),this._target=t,this._target.addEventListener("touchstart",this._boundEventHandler),this._target.addEventListener("touchmove",this._boundEventHandler),this._target.addEventListener("touchend",this._boundEventHandler),this._target.addEventListener("touchcancel",this._boundEventHandler)}detach(){this._target&&(this._stopLongpressTimeout(),this._stopTwoTouchTimeout(),this._target.removeEventListener("touchstart",this._boundEventHandler),this._target.removeEventListener("touchmove",this._boundEventHandler),this._target.removeEventListener("touchend",this._boundEventHandler),this._target.removeEventListener("touchcancel",this._boundEventHandler),this._target=null)}_eventHandler(t){let e;switch(t.stopPropagation(),t.preventDefault(),t.type){case"touchstart":e=this._touchStart;break;case"touchmove":e=this._touchMove;break;case"touchend":case"touchcancel":e=this._touchEnd;break}for(let s=0;s0&&Date.now()-this._tracked[0].started>250){this._state=0,this._ignored.push(t);return}if(this._waitingRelease){this._state=0,this._ignored.push(t);return}switch(this._tracked.push({id:t,started:Date.now(),active:!0,firstX:e,firstY:s,lastX:e,lastY:s,angle:0}),this._tracked.length){case 1:this._startLongpressTimeout();break;case 2:this._state&=-26,this._stopLongpressTimeout();break;case 3:this._state&=-99;break;default:this._state=0}}_touchMove(t,e,s){let r=this._tracked.find(h=>h.id===t);if(r===void 0)return;r.lastX=e,r.lastY=s;let n=e-r.firstX,a=s-r.firstY;if((r.firstX!==r.lastX||r.firstY!==r.lastY)&&(r.angle=Math.atan2(a,n)*180/Math.PI),!this._hasDetectedGesture()){if(Math.hypot(n,a)<50)return;if(this._state&=-24,this._stopLongpressTimeout(),this._tracked.length!==1&&(this._state&=-9),this._tracked.length!==2&&(this._state&=-97),this._tracked.length===2){let h=this._tracked.find(o=>o.id!==t);if(Math.hypot(h.firstX-h.lastX,h.firstY-h.lastY)>50){let o=Math.abs(r.angle-h.angle);o=Math.abs((o+180)%360-180),o>90?this._state&=-33:this._state&=-65,this._isTwoTouchTimeoutRunning()&&this._stopTwoTouchTimeout()}else this._isTwoTouchTimeoutRunning()||this._startTwoTouchTimeout()}if(!this._hasDetectedGesture())return;this._pushEvent("gesturestart")}this._pushEvent("gesturemove")}_touchEnd(t,e,s){if(this._ignored.indexOf(t)!==-1){this._ignored.splice(this._ignored.indexOf(t),1),this._ignored.length===0&&this._tracked.length===0&&(this._state=127,this._waitingRelease=!1);return}if(!this._hasDetectedGesture()&&this._isTwoTouchTimeoutRunning()&&(this._stopTwoTouchTimeout(),this._state=0),!this._hasDetectedGesture()&&(this._state&=-105,this._state&=-17,this._stopLongpressTimeout(),!this._waitingRelease))switch(this._releaseStart=Date.now(),this._waitingRelease=!0,this._tracked.length){case 1:this._state&=-7;break;case 2:this._state&=-6;break}if(this._waitingRelease){Date.now()-this._releaseStart>250&&(this._state=0),this._tracked.some(n=>Date.now()-n.started>1e3)&&(this._state=0);let r=this._tracked.find(n=>n.id===t);if(r.active=!1,this._hasDetectedGesture())this._pushEvent("gesturestart");else if(this._state!==0)return}this._hasDetectedGesture()&&this._pushEvent("gestureend");for(let r=0;rt.active))}_startLongpressTimeout(){this._stopLongpressTimeout(),this._longpressTimeoutId=setTimeout(()=>this._longpressTimeout(),1e3)}_stopLongpressTimeout(){clearTimeout(this._longpressTimeoutId),this._longpressTimeoutId=null}_longpressTimeout(){if(this._hasDetectedGesture())throw new Error("A longpress gesture failed, conflict with a different gesture");this._state=16,this._pushEvent("gesturestart")}_startTwoTouchTimeout(){this._stopTwoTouchTimeout(),this._twoTouchTimeoutId=setTimeout(()=>this._twoTouchTimeout(),50)}_stopTwoTouchTimeout(){clearTimeout(this._twoTouchTimeoutId),this._twoTouchTimeoutId=null}_isTwoTouchTimeoutRunning(){return this._twoTouchTimeoutId!==null}_twoTouchTimeout(){if(this._tracked.length===0)throw new Error("A pinch or two drag gesture failed, no tracked touches");let t=this._getAverageMovement(),e=Math.abs(t.x),s=Math.abs(t.y),r=this._getAverageDistance(),n=Math.abs(Math.hypot(r.first.x,r.first.y)-Math.hypot(r.last.x,r.last.y));s{this._target&&(e=document.elementFromPoint(t.clientX,t.clientY),this._updateVisibility(e))},0)}_showCursor(){this._canvas.style.visibility==="hidden"&&(this._canvas.style.visibility="")}_hideCursor(){this._canvas.style.visibility!=="hidden"&&(this._canvas.style.visibility="hidden")}_shouldShowCursor(t){return t?t===this._target?!0:!(!this._target.contains(t)||window.getComputedStyle(t).cursor!=="none"):!1}_updateVisibility(t){this._captureIsActive()&&(t=document.captureElement),this._shouldShowCursor(t)?this._showCursor():this._hideCursor()}_updatePosition(){this._canvas.style.left=this._position.x+"px",this._canvas.style.top=this._position.y+"px"}_captureIsActive(){return document.captureElement&&document.documentElement.contains(document.captureElement)}};var zi=40*1024*1024,Yt={CONNECTING:"connecting",OPEN:"open",CLOSING:"closing",CLOSED:"closed"},jt={CONNECTING:[WebSocket.CONNECTING,Yt.CONNECTING],OPEN:[WebSocket.OPEN,Yt.OPEN],CLOSING:[WebSocket.CLOSING,Yt.CLOSING],CLOSED:[WebSocket.CLOSED,Yt.CLOSED]},Xr=["send","close","binaryType","onerror","onmessage","onopen","protocol","readyState"],yt=class{constructor(){this._websocket=null,this._rQi=0,this._rQlen=0,this._rQbufferSize=1024*1024*4,this._rQ=null,this._sQbufferSize=1024*10,this._sQlen=0,this._sQ=null,this._eventHandlers={message:()=>{},open:()=>{},close:()=>{},error:()=>{}}}get readyState(){let t;return this._websocket===null?"unused":(t=this._websocket.readyState,jt.CONNECTING.includes(t)?"connecting":jt.OPEN.includes(t)?"open":jt.CLOSING.includes(t)?"closing":jt.CLOSED.includes(t)?"closed":"unknown")}rQpeek8(){return this._rQ[this._rQi]}rQskipBytes(t){this._rQi+=t}rQshift8(){return this._rQshift(1)}rQshift16(){return this._rQshift(2)}rQshift32(){return this._rQshift(4)}_rQshift(t){let e=0;for(let s=t-1;s>=0;s--)e+=this._rQ[this._rQi++]<>>0}rQshiftStr(t){let e="";for(let s=0;s>8&255,this._sQ[this._sQlen++]=t>>0&255}sQpush32(t){this._sQensureSpace(4),this._sQ[this._sQlen++]=t>>24&255,this._sQ[this._sQlen++]=t>>16&255,this._sQ[this._sQlen++]=t>>8&255,this._sQ[this._sQlen++]=t>>0&255}sQpushString(t){let e=t.split("").map(s=>s.charCodeAt(0));this.sQpushBytes(new Uint8Array(e))}sQpushBytes(t){for(let e=0;et.length-e&&(s=t.length-e),this._sQ.set(t.subarray(e,e+s),this._sQlen),this._sQlen+=s,e+=s}}flush(){this._sQlen>0&&this.readyState==="open"&&(this._websocket.send(new Uint8Array(this._sQ.buffer,0,this._sQlen)),this._sQlen=0)}_sQensureSpace(t){this._sQbufferSize-this._sQlen{}}on(t,e){this._eventHandlers[t]=e}_allocateBuffers(){this._rQ=new Uint8Array(this._rQbufferSize),this._sQ=new Uint8Array(this._sQbufferSize)}init(){this._allocateBuffers(),this._rQi=0,this._websocket=null}open(t,e){this.attach(new WebSocket(t,e))}attach(t){this.init();let e=[...Object.keys(t),...Object.getOwnPropertyNames(Object.getPrototypeOf(t))];for(let s=0;s{L(">> WebSock.onopen"),this._websocket.protocol&&V("Server choose sub-protocol: "+this._websocket.protocol),this._eventHandlers.open(),L("<< WebSock.onopen")},this._websocket.onclose=s=>{L(">> WebSock.onclose"),this._eventHandlers.close(s),L("<< WebSock.onclose")},this._websocket.onerror=s=>{L(">> WebSock.onerror: "+s),this._eventHandlers.error(s),L("<< WebSock.onerror: "+s)}}close(){this._websocket&&((this.readyState==="connecting"||this.readyState==="open")&&(V("Closing WebSocket connection"),this._websocket.close()),this._websocket.onmessage=()=>{})}_expandCompactRQ(t){let e=(this._rQlen-this._rQi+t)*8,s=this._rQbufferSizezi&&(this._rQbufferSize=zi,this._rQbufferSize-(this._rQlen-this._rQi)this._rQbufferSize-this._rQlen&&this._expandCompactRQ(e.length),this._rQ.set(e,this._rQlen),this._rQlen+=e.length,this._rQlen-this._rQi>0?this._eventHandlers.message():L("Ignoring empty message")}};var Kr={Again:57349,AltLeft:56,AltRight:57400,ArrowDown:57424,ArrowLeft:57419,ArrowRight:57421,ArrowUp:57416,AudioVolumeDown:57390,AudioVolumeMute:57376,AudioVolumeUp:57392,Backquote:41,Backslash:43,Backspace:14,BracketLeft:26,BracketRight:27,BrowserBack:57450,BrowserFavorites:57446,BrowserForward:57449,BrowserHome:57394,BrowserRefresh:57447,BrowserSearch:57445,BrowserStop:57448,CapsLock:58,Comma:51,ContextMenu:57437,ControlLeft:29,ControlRight:57373,Convert:121,Copy:57464,Cut:57404,Delete:57427,Digit0:11,Digit1:2,Digit2:3,Digit3:4,Digit4:5,Digit5:6,Digit6:7,Digit7:8,Digit8:9,Digit9:10,Eject:57469,End:57423,Enter:28,Equal:13,Escape:1,F1:59,F10:68,F11:87,F12:88,F13:93,F14:94,F15:95,F16:85,F17:57347,F18:57463,F19:57348,F2:60,F20:90,F21:116,F22:57465,F23:109,F24:111,F3:61,F4:62,F5:63,F6:64,F7:65,F8:66,F9:67,Find:57409,Help:57461,Hiragana:119,Home:57415,Insert:57426,IntlBackslash:86,IntlRo:115,IntlYen:125,KanaMode:112,Katakana:120,KeyA:30,KeyB:48,KeyC:46,KeyD:32,KeyE:18,KeyF:33,KeyG:34,KeyH:35,KeyI:23,KeyJ:36,KeyK:37,KeyL:38,KeyM:50,KeyN:49,KeyO:24,KeyP:25,KeyQ:16,KeyR:19,KeyS:31,KeyT:20,KeyU:22,KeyV:47,KeyW:17,KeyX:45,KeyY:21,KeyZ:44,Lang1:114,Lang2:113,Lang3:120,Lang4:119,Lang5:118,LaunchApp1:57451,LaunchApp2:57377,LaunchMail:57452,MediaPlayPause:57378,MediaSelect:57453,MediaStop:57380,MediaTrackNext:57369,MediaTrackPrevious:57360,MetaLeft:57435,MetaRight:57436,Minus:12,NonConvert:123,NumLock:69,Numpad0:82,Numpad1:79,Numpad2:80,Numpad3:81,Numpad4:75,Numpad5:76,Numpad6:77,Numpad7:71,Numpad8:72,Numpad9:73,NumpadAdd:78,NumpadComma:126,NumpadDecimal:83,NumpadDivide:57397,NumpadEnter:57372,NumpadEqual:89,NumpadMultiply:55,NumpadParenLeft:57462,NumpadParenRight:57467,NumpadSubtract:74,Open:100,PageDown:57425,PageUp:57417,Paste:101,Pause:57414,Period:52,Power:57438,PrintScreen:84,Props:57350,Quote:40,ScrollLock:70,Semicolon:39,ShiftLeft:42,ShiftRight:54,Slash:53,Sleep:57439,Space:57,Suspend:57381,Tab:15,Undo:57351,WakeUp:57443};var R={encodingRaw:0,encodingCopyRect:1,encodingRRE:2,encodingHextile:5,encodingZlib:6,encodingTight:7,encodingZRLE:16,encodingTightPNG:-260,encodingJPEG:21,encodingH264:50,pseudoEncodingQualityLevel9:-23,pseudoEncodingQualityLevel0:-32,pseudoEncodingDesktopSize:-223,pseudoEncodingLastRect:-224,pseudoEncodingCursor:-239,pseudoEncodingQEMUExtendedKeyEvent:-258,pseudoEncodingQEMULedEvent:-261,pseudoEncodingDesktopName:-307,pseudoEncodingExtendedDesktopSize:-308,pseudoEncodingXvp:-309,pseudoEncodingFence:-312,pseudoEncodingContinuousUpdates:-313,pseudoEncodingExtendedMouseButtons:-316,pseudoEncodingCompressLevel9:-247,pseudoEncodingCompressLevel0:-256,pseudoEncodingVMwareCursor:1464686180,pseudoEncodingExtendedClipboard:3231835598};var qt=class i{constructor(){this._key=null}get algorithm(){return{name:"AES-ECB"}}static async importKey(t,e,s,r){let n=new i;return await n._importKey(t,s,r),n}async _importKey(t,e,s){this._key=await window.crypto.subtle.importKey("raw",t,{name:"AES-CBC"},e,s)}async encrypt(t,e){let s=new Uint8Array(e);if(s.length%16!==0||this._key===null)return null;let r=s.length/16;for(let n=0;n>>6;for(let n=0;n<15;n++)e[n]=t[n+1]>>6|t[n]<<2,t[n]=t[n+1]>>7|t[n]<<1;let r=[0,135,14,137];e[14]^=s>>>1,e[15]=t[15]<<2^r[s],t[15]=t[15]<<1^r[s>>1],this._k1=t,this._k2=e}async _encryptCTR(t,e){let s=await window.crypto.subtle.encrypt({name:"AES-CTR",counter:e,length:128},this._ctrKey,t);return new Uint8Array(s)}async _decryptCTR(t,e){let s=await window.crypto.subtle.decrypt({name:"AES-CTR",counter:e,length:128},this._ctrKey,t);return new Uint8Array(s)}async _computeCMAC(t,e){if(e.length!==16)return null;let s=Math.floor(t.length/16),r=Math.ceil(t.length/16),n=t.length-s*16,a=new Uint8Array((r+1)*16);if(a.set(e),a.set(t,16),n===0)for(let o=0;o<16;o++)a[s*16+o]^=this._k1[o];else{a[(s+1)*16+n]=128;for(let o=0;o<16;o++)a[(s+1)*16+o]^=this._k2[o]}let h=await window.crypto.subtle.encrypt({name:"AES-CBC",iv:this._zeroBlock},this._cbcKey,a);return h=new Uint8Array(h),h.slice(h.length-32,h.length-16)}static async importKey(t,e,s,r){let n=new i;return await n._importKey(t),n}async _importKey(t){this._rawKey=t,this._ctrKey=await window.crypto.subtle.importKey("raw",t,{name:"AES-CTR"},!1,["encrypt","decrypt"]),this._cbcKey=await window.crypto.subtle.importKey("raw",t,{name:"AES-CBC"},!1,["encrypt"]),await this._initCMAC()}async encrypt(t,e){let s=t.additionalData,r=t.iv,n=await this._computeCMAC(r,this._prefixBlock0),a=await this._encryptCTR(e,n),h=await this._computeCMAC(s,this._prefixBlock1),_=await this._computeCMAC(a,this._prefixBlock2);for(let l=0;l<16;l++)_[l]^=n[l]^h[l];let o=new Uint8Array(16+a.length);return o.set(a),o.set(_,a.length),o}async decrypt(t,e){let s=e.slice(0,e.length-16),r=t.additionalData,n=t.iv,a=e.slice(e.length-16),h=await this._computeCMAC(n,this._prefixBlock0),_=await this._computeCMAC(r,this._prefixBlock1),o=await this._computeCMAC(s,this._prefixBlock2);for(let x=0;x<16;x++)o[x]^=h[x]^_[x];if(o.length!==a.length)return null;for(let x=0;x>>3]&1<>>10,this.keys[h]|=(o&4032)>>>6,++h,this.keys[h]=(_&258048)<<12,this.keys[h]|=(_&63)<<16,this.keys[h]|=(o&258048)>>>4,this.keys[h]|=o&63,++h}}enc8(t){let e=t.slice(),s=0,r,n,a;r=e[s++]<<24|e[s++]<<16|e[s++]<<8|e[s++],n=e[s++]<<24|e[s++]<<16|e[s++]<<8|e[s++],a=(r>>>4^n)&252645135,n^=a,r^=a<<4,a=(r>>>16^n)&65535,n^=a,r^=a<<16,a=(n>>>2^r)&858993459,r^=a,n^=a<<2,a=(n>>>8^r)&16711935,r^=a,n^=a<<8,n=n<<1|n>>>31&1,a=(r^n)&2863311530,r^=a,n^=a,r=r<<1|r>>>31&1;for(let h=0,_=0;h<8;++h){a=n<<28|n>>>4,a^=this.keys[_++];let o=Tr[a&63];o|=Cr[a>>>8&63],o|=Er[a>>>16&63],o|=kr[a>>>24&63],a=n^this.keys[_++],o|=Mr[a&63],o|=Lr[a>>>8&63],o|=Ar[a>>>16&63],o|=Sr[a>>>24&63],r^=o,a=r<<28|r>>>4,a^=this.keys[_++],o=Tr[a&63],o|=Cr[a>>>8&63],o|=Er[a>>>16&63],o|=kr[a>>>24&63],a=r^this.keys[_++],o|=Mr[a&63],o|=Lr[a>>>8&63],o|=Ar[a>>>16&63],o|=Sr[a>>>24&63],n^=o}for(n=n<<31|n>>>1,a=(r^n)&2863311530,r^=a,n^=a,r=r<<31|r>>>1,a=(r>>>8^n)&16711935,n^=a,r^=a<<8,a=(r>>>2^n)&858993459,n^=a,r^=a<<2,a=(n>>>16^r)&65535,r^=a,n^=a<<16,a=(n>>>4^r)&252645135,r^=a,n^=a<<4,a=[n,r],s=0;s<8;s++)e[s]=(a[s>>>2]>>>8*(3-s%4))%256,e[s]<0&&(e[s]+=256);return e}},ei=class i{constructor(){this._cipher=null}get algorithm(){return{name:"DES-ECB"}}static importKey(t,e,s,r){let n=new i;return n._importKey(t),n}_importKey(t,e,s){this._cipher=new $t(t)}encrypt(t,e){let s=new Uint8Array(e);if(s.length%8!==0||this._cipher===null)return null;let r=s.length/8;for(let n=0;n0n;)(t&1n)===1n&&(s=s*i%e),t=t>>1n,i=i*i%e;return s}function tt(i,t=0){let e=i.toString(16);t===0&&(t=Math.ceil(e.length/2)),e=e.padStart(t*2,"0");let s=e.length/2,r=new Uint8Array(s);for(let n=0;nthis._keyBytes-11)return null;let s=new Uint8Array(this._keyBytes-e.length-3);window.crypto.getRandomValues(s);for(let h=0;hthis._keyBytes?s:this._keyBytes,a=et(ne(r),this._privateKeyBigInt,this._pBigInt);return tt(a,n).slice(0,n)}};async function Rr(i){let t="";for(let e=0;e>2);for(let e=0;e>5]|=(255&i.charCodeAt(e/8))<>5]>>>e%32&255);return t}function pa(i,t){i[t>>5]|=128<>>9<<4)]=t;let e=1732584193,s=-271733879,r=-1732584194,n=271733878;for(let a=0;a>16)+(t>>16)+(e>>16)<<16|65535&e}function ga(i,t){return i<>>32-t}var Vi=class{constructor(){this._algorithms={"AES-ECB":qt,"AES-EAX":Jt,"DES-ECB":ei,"DES-CBC":ti,"RSA-PKCS1-v1_5":ii,DH:si,MD5:Rr}}encrypt(t,e,s){if(e.algorithm.name!==t.name)throw new Error("algorithm does not match");if(typeof e.encrypt!="function")throw new Error("key does not support encryption");return e.encrypt(t,s)}decrypt(t,e,s){if(e.algorithm.name!==t.name)throw new Error("algorithm does not match");if(typeof e.decrypt!="function")throw new Error("key does not support encryption");return e.decrypt(t,s)}importKey(t,e,s,r,n){if(t!=="raw")throw new Error("key format is not supported");let a=this._algorithms[s.name];if(typeof a>"u"||typeof a.importKey!="function")throw new Error("algorithm is not supported");return a.importKey(e,s,r,n)}generateKey(t,e,s){let r=this._algorithms[t.name];if(typeof r>"u"||typeof r.generateKey!="function")throw new Error("algorithm is not supported");return r.generateKey(t,e,s)}exportKey(t,e){if(t!=="raw")throw new Error("key format is not supported");if(typeof e.exportKey!="function")throw new Error("key does not support exportKey");return e.exportKey()}digest(t,e){let s=this._algorithms[t];if(typeof s!="function")throw new Error("algorithm is not supported");return s(e)}deriveBits(t,e,s){if(e.algorithm.name!==t.name)throw new Error("algorithm does not match");if(typeof e.deriveBits!="function")throw new Error("key does not support deriveBits");return e.deriveBits(t,s)}},O=new Vi;var ni=class{constructor(){this._cipher=null,this._counter=new Uint8Array(16)}async setKey(t){this._cipher=await O.importKey("raw",t,{name:"AES-EAX"},!1,["encrypt, decrypt"])}async makeMessage(t){let e=new Uint8Array([(t.length&65280)>>>8,t.length&255]),s=await O.encrypt({name:"AES-EAX",iv:this._counter,additionalData:e},this._cipher,t);for(let n=0;n<16&&this._counter[n]++===255;n++);let r=new Uint8Array(t.length+2+16);return r.set(e),r.set(s,2),r}async receiveMessage(t,e){let s=new Uint8Array([(t&65280)>>>8,t&255]),r=await O.decrypt({name:"AES-EAX",iv:this._counter,additionalData:s},this._cipher,e);for(let n=0;n<16&&this._counter[n]++===255;n++);return r}},Xt=class extends Ee{constructor(t,e){super(),this._hasStarted=!1,this._checkSock=null,this._checkCredentials=null,this._approveServerResolve=null,this._sockReject=null,this._credentialsReject=null,this._approveServerReject=null,this._sock=t,this._getCredentials=e}_waitSockAsync(t){return new Promise((e,s)=>{let r=()=>!this._sock.rQwait("RA2",t);r()?e():(this._checkSock=()=>{r()&&(e(),this._checkSock=null,this._sockReject=null)},this._sockReject=s)})}_waitApproveKeyAsync(){return new Promise((t,e)=>{this._approveServerResolve=t,this._approveServerReject=e})}_waitCredentialsAsync(t){let e=()=>t===1&&this._getCredentials().username!==void 0&&this._getCredentials().password!==void 0?!0:t===2&&this._getCredentials().password!==void 0;return new Promise((s,r)=>{e()?s():(this._checkCredentials=()=>{e()&&(s(),this._checkCredentials=null,this._credentialsReject=null)},this._credentialsReject=r)})}checkInternalEvents(){this._checkSock!==null&&this._checkSock(),this._checkCredentials!==null&&this._checkCredentials()}approveServer(){this._approveServerResolve!==null&&(this._approveServerResolve(),this._approveServerResolve=null)}disconnect(){this._sockReject!==null&&(this._sockReject(new Error("disconnect normally")),this._sockReject=null),this._credentialsReject!==null&&(this._credentialsReject(new Error("disconnect normally")),this._credentialsReject=null),this._approveServerReject!==null&&(this._approveServerReject(new Error("disconnect normally")),this._approveServerReject=null)}async negotiateRA2neAuthAsync(){this._hasStarted=!0,await this._waitSockAsync(4);let t=this._sock.rQpeekBytes(4),e=this._sock.rQshift32();if(e<1024)throw new Error("RA2: server public key is too short: "+e);if(e>8192)throw new Error("RA2: server public key is too long: "+e);let s=Math.ceil(e/8);await this._waitSockAsync(s*2);let r=this._sock.rQshiftBytes(s),n=this._sock.rQshiftBytes(s),a=await O.importKey("raw",{n:r,e:n},{name:"RSA-PKCS1-v1_5"},!1,["encrypt"]),h=new Uint8Array(4+s*2);h.set(t),h.set(r,4),h.set(n,4+s);let _=this._waitApproveKeyAsync();this.dispatchEvent(new CustomEvent("serververification",{detail:{type:"RSA",publickey:h}})),await _;let o=2048,l=Math.ceil(o/8),x=(await O.generateKey({name:"RSA-PKCS1-v1_5",modulusLength:o,publicExponent:new Uint8Array([1,0,1])},!0,["encrypt"])).privateKey,u=await O.exportKey("raw",x),d=u.n,X=u.e,F=new Uint8Array(4+l*2);F[0]=(o&4278190080)>>>24,F[1]=(o&16711680)>>>16,F[2]=(o&65280)>>>8,F[3]=o&255,F.set(d,4),F.set(X,4+l),this._sock.sQpushBytes(F),this._sock.flush();let A=new Uint8Array(16);window.crypto.getRandomValues(A);let k=await O.encrypt({name:"RSA-PKCS1-v1_5"},a,A),T=new Uint8Array(2+s);if(T[0]=(s&65280)>>>8,T[1]=s&255,T.set(k,2),this._sock.sQpushBytes(T),this._sock.flush(),await this._waitSockAsync(2),this._sock.rQshift16()!==l)throw new Error("RA2: wrong encrypted message length");let E=this._sock.rQshiftBytes(l),K=await O.decrypt({name:"RSA-PKCS1-v1_5"},x,E);if(K===null||K.length!==16)throw new Error("RA2: corrupted server encrypted random");let C=new Uint8Array(32),I=new Uint8Array(32);C.set(K),C.set(A,16),I.set(A),I.set(K,16),C=await window.crypto.subtle.digest("SHA-1",C),C=new Uint8Array(C).slice(0,16),I=await window.crypto.subtle.digest("SHA-1",I),I=new Uint8Array(I).slice(0,16);let S=new ni;await S.setKey(C);let D=new ni;await D.setKey(I);let B=new Uint8Array(8+s*2+l*2),M=new Uint8Array(8+s*2+l*2);if(B.set(h),B.set(F,4+s*2),M.set(F),M.set(h,4+l*2),B=await window.crypto.subtle.digest("SHA-1",B),M=await window.crypto.subtle.digest("SHA-1",M),B=new Uint8Array(B),M=new Uint8Array(M),this._sock.sQpushBytes(await S.makeMessage(M)),this._sock.flush(),await this._waitSockAsync(38),this._sock.rQshift16()!==20)throw new Error("RA2: wrong server hash");let z=await D.receiveMessage(20,this._sock.rQshiftBytes(36));if(z===null)throw new Error("RA2: failed to authenticate the message");for(let Z=0;Z<20;Z++)if(z[Z]!==B[Z])throw new Error("RA2: wrong server hash");if(await this._waitSockAsync(19),this._sock.rQshift16()!==1)throw new Error("RA2: wrong subtype");let te=await D.receiveMessage(1,this._sock.rQshiftBytes(17));if(te===null)throw new Error("RA2: failed to authenticate the message");te=te[0];let Ie=this._waitCredentialsAsync(te);if(te===1)(this._getCredentials().username===void 0||this._getCredentials().password===void 0)&&this.dispatchEvent(new CustomEvent("credentialsrequired",{detail:{types:["username","password"]}}));else if(te===2)this._getCredentials().password===void 0&&this.dispatchEvent(new CustomEvent("credentialsrequired",{detail:{types:["password"]}}));else throw new Error("RA2: wrong subtype");await Ie;let oe;te===1?oe=he(this._getCredentials().username).slice(0,255):oe="";let Fe=he(this._getCredentials().password).slice(0,255),_e=new Uint8Array(oe.length+Fe.length+2);_e[0]=oe.length,_e[oe.length+1]=Fe.length;for(let Z=0;Z0;){if(n.rQwait("RAW",o))return!1;let l=e+(r-this._lines),x=n.rQshiftBytes(o,!1);if(h==8){let u=new Uint8Array(s*4);for(let d=0;d>0&3)*255/3,u[d*4+1]=(x[d]>>2&3)*255/3,u[d*4+2]=(x[d]>>4&3)*255/3,u[d*4+3]=255;x=u}for(let u=0;u0;){if(n.rQwait("RRE",12))return!1;let _=n.rQshiftBytes(4),o=n.rQshift16(),l=n.rQshift16(),x=n.rQshift16(),u=n.rQshift16();a.fillRect(t+o,e+l,x,u,_),this._subrects--}return!0}};var St=class{constructor(){this._tiles=0,this._lastsubencoding=0,this._tileBuffer=new Uint8Array(16*16*4)}decodeRect(t,e,s,r,n,a,h){for(this._tiles===0&&(this._tilesX=Math.ceil(s/16),this._tilesY=Math.ceil(r/16),this._totalTiles=this._tilesX*this._tilesY,this._tiles=this._totalTiles);this._tiles>0;){let _=1;if(n.rQwait("HEXTILE",_))return!1;let o=n.rQpeek8();if(o>30)throw new Error("Illegal hextile subencoding (subencoding: "+o+")");let l=this._totalTiles-this._tiles,x=l%this._tilesX,u=Math.floor(l/this._tilesX),d=t+x*16,X=e+u*16,F=Math.min(16,t+s-d),A=Math.min(16,e+r-X);if(o&1)_+=F*A*4;else if(o&2&&(_+=4),o&4&&(_+=4),o&8){if(_++,n.rQwait("HEXTILE",_))return!1;let k=n.rQpeekBytes(_).at(-1);o&16?_+=k*6:_+=k*2}if(n.rQwait("HEXTILE",_))return!1;if(n.rQshift8(),o===0)this._lastsubencoding&1?L(" Ignoring blank after RAW"):a.fillRect(d,X,F,A,this._background);else if(o&1){let k=F*A,T=n.rQshiftBytes(k*4,!1);for(let E=0;E>4,I=K&15,S=n.rQshift8(),D=(S>>4)+1,B=(S&15)+1;this._subTile(C,I,D,B,E)}}this._finishTile(a)}this._lastsubencoding=o,this._tiles--}return!0}_startTile(t,e,s,r,n){this._tileX=t,this._tileY=e,this._tileW=s,this._tileH=r;let a=n[0],h=n[1],_=n[2],o=this._tileBuffer;for(let l=0;l>o&1&&(this._zlibs[o].reset(),V("Reset zlib stream "+o));this._ctl=this._ctl>>4}let _;if(this._ctl===8)_=this._fillRect(t,e,s,r,n,a,h);else if(this._ctl===9)_=this._jpegRect(t,e,s,r,n,a,h);else if(this._ctl===10)_=this._pngRect(t,e,s,r,n,a,h);else if(!(this._ctl&8))_=this._basicRect(this._ctl,t,e,s,r,n,a,h);else throw new Error("Illegal tight compression received (ctl: "+this._ctl+")");return _&&(this._ctl=null),_}_fillRect(t,e,s,r,n,a,h){if(n.rQwait("TIGHT",3))return!1;let _=n.rQshiftBytes(3);return a.fillRect(t,e,s,r,_,!1),!0}_jpegRect(t,e,s,r,n,a,h){let _=this._readData(n);return _===null?!1:(a.imageRect(t,e,s,r,"image/jpeg",_),!0)}_pngRect(t,e,s,r,n,a,h){throw new Error("PNG received in standard Tight rect")}_basicRect(t,e,s,r,n,a,h,_){if(this._filter===null)if(t&4){if(a.rQwait("TIGHT",1))return!1;this._filter=a.rQshift8()}else this._filter=0;let o=t&3,l;switch(this._filter){case 0:l=this._copyFilter(o,e,s,r,n,a,h,_);break;case 1:l=this._paletteFilter(o,e,s,r,n,a,h,_);break;case 2:l=this._gradientFilter(o,e,s,r,n,a,h,_);break;default:throw new Error("Illegal tight filter received (ctl: "+this._filter+")")}return l&&(this._filter=null),l}_copyFilter(t,e,s,r,n,a,h,_){let o=r*n*3,l;if(o===0)return!0;if(o<12){if(a.rQwait("TIGHT",o))return!1;l=a.rQshiftBytes(o)}else{if(l=this._readData(a),l===null)return!1;this._zlibs[t].setInput(l),l=this._zlibs[t].inflate(o),this._zlibs[t].setInput(null)}let x=new Uint8Array(r*n*4);for(let u=0,d=0;u=0;F--)u=(x*s+X*8+7-F)*4,d=(n[x*o+X]>>F&1)*3,_[u]=a[d],_[u+1]=a[d+1],_[u+2]=a[d+2],_[u+3]=255;for(let F=7;F>=8-s%8;F--)u=(x*s+X*8+7-F)*4,d=(n[x*o+X]>>F&1)*3,_[u]=a[d],_[u+1]=a[d+1],_[u+2]=a[d+2],_[u+3]=255}h.blitImage(t,e,s,r,_,0,!1)}_paletteRect(t,e,s,r,n,a,h){let _=this._getScratchBuffer(s*r*4),o=s*r*4;for(let l=0,x=0;l255&&(C=255);let I=l[d++]+C;x[u++]=I,k[K]=A[K],X[K]=I}x[u++]=255,F++}}return h.blitImage(e,s,r,n,x,0,!1),!0}_readData(t){if(this._len===0){if(t.rQwait("TIGHT",3))return null;let s;s=t.rQshift8(),this._len=s&127,s&128&&(s=t.rQshift8(),this._len|=(s&127)<<7,s&128&&(s=t.rQshift8(),this._len|=s<<14))}if(t.rQwait("TIGHT",this._len))return null;let e=t.rQshiftBytes(this._len,!1);return this._len=0,e}_getScratchBuffer(t){return(!this._scratchBuffer||this._scratchBuffer.length=2&&X<=16){let F=this._decodePaletteTile(X,d,u,l);a.blitImage(x,o,u,l,F,0,!1)}else if(X===128){let F=this._decodeRLETile(d);a.blitImage(x,o,u,l,F,0,!1)}else if(X>=130&&X<=255){let F=this._decodeRLEPaletteTile(X-128,d);a.blitImage(x,o,u,l,F,0,!1)}else throw new Error("Unknown subencoding: "+X)}}return this._length=0,!0}_getBitsPerPixelInPalette(t){if(t<=2)return 1;if(t<=4)return 2;if(t<=16)return 4}_readPixels(t){let e=this._pixelBuffer,s=this._inflator.inflate(3*t);for(let r=0,n=0;r>u&_;n[o]=a[X*4],n[o+1]=a[X*4+1],n[o+2]=a[X*4+2],n[o+3]=a[X*4+3],o+=4,u-=h}u<8-h&&x=128&&(a-=128,h=this._readRLELength()),a>t)throw new Error("Too big index in palette: "+a+", palette size: "+t);if(n+h>e)throw new Error("Too big rle length in palette mode: "+h+", allowed length is: "+(e-n));for(let _=0;_d[1]==192||d[1]==194);if(l==-1)throw new Error("Illegal JPEG image without SOF");o.length===0&&this._segments.splice(l+1,0,...this._cachedQuantTables),_.length===0&&this._segments.splice(l+1,0,...this._cachedHuffmanTables);let x=0;for(let d of this._segments)x+=d.length;let u=new Uint8Array(x);x=0;for(let d of this._segments)u.set(d,x),x+=d.length;return a.imageRect(t,e,s,r,"image/jpeg",u),_.length!==0&&(this._cachedHuffmanTables=_),o.length!==0&&(this._cachedQuantTables=o),this._segments=[],!0}_readSegment(t){if(t.rQwait("JPEG",2))return null;let e=t.rQshift8();if(e!=255)throw new Error("Illegal JPEG marker received (byte: "+e+")");let s=t.rQshift8();if(s>=208&&s<=217||s==1)return new Uint8Array([e,s]);if(t.rQwait("JPEG",2,2))return null;let r=t.rQshift16();if(r<2)throw new Error("Illegal JPEG length received (length: "+r+")");if(t.rQwait("JPEG",r-2,4))return null;let n=0;if(s===218)for(n+=2;;){if(t.rQwait("JPEG",r-2+n,4))return null;let h=t.rQpeekBytes(r-2+n,!1);if(h.at(-2)===255&&h.at(-1)!==0&&!(h.at(-1)>=208&&h.at(-1)<=215)){n-=2;break}n++}let a=new Uint8Array(2+r+n);return a[0]=e,a[1]=s,a[2]=r>>8,a[3]=r,a.set(t.rQshiftBytes(r-2+n,!1),4),a}};var Wi=class{constructor(t){this._data=t,this._index=0,this.profileIdc=null,this.constraintSet=null,this.levelIdc=null}_getStartSequenceLen(t){let e=this._data;return e[t+0]==0&&e[t+1]==0&&e[t+2]==0&&e[t+3]==1?4:e[t+0]==0&&e[t+1]==0&&e[t+2]==1?3:0}_indexOfNextNalUnit(t){let e=this._data;for(let s=t;sthis._handleFrame(n),error:n=>this._handleError(n)}));let r="avc1."+t.toString(16).padStart(2,"0")+e.toString(16).padStart(2,"0")+s.toString(16).padStart(2,"0");this._decoder.configure({codec:r,codedWidth:this._width,codedHeight:this._height,optimizeForLatency:!0})}_preparePendingFrame(t){let e={timestamp:t,promise:null,resolve:null,frame:null,ready:!1,keep:!1};return e.promise=new Promise(s=>{e.resolve=s}),this._pendingFrames.push(e),e}decode(t){let e=new Wi(t),s=null,r=Math.round(window.performance.now()*1e3);for(;;){let n=e.parse();if(n===null)break;if(e.profileIdc!==null&&(self._profileIdc=e.profileIdc,self._constraintSet=e.constraintSet,self._levelIdc=e.levelIdc),this._decoder===null||this._decoder.state!=="configured"){if(!n.key){W("Missing key frame. Can't decode until one arrives");continue}if(self._profileIdc===null){W("Cannot config decoder. Have not received SPS and PPS yet.");continue}this._configureDecoder(self._profileIdc,self._constraintSet,self._levelIdc)}s=this._preparePendingFrame(r);let a=new EncodedVideoChunk({timestamp:r,type:n.key?"key":"delta",data:n.frame});try{this._decoder.decode(a)}catch(h){W("Failed to decode:",h)}}return s!==null&&(s.keep=!0),s}},Tt=class{constructor(){this._tick=0,this._contexts={}}_contextId(t,e,s,r){return[t,e,s,r].join(",")}_findOldestContextId(){let t=Number.MAX_VALUE,e;for(let[s,r]of Object.entries(this._contexts))r.lastUsed=64){let h=this._findOldestContextId();delete this._contexts[h]}let a=new Zi(s,r);return this._contexts[this._contextId(t,e,s,r)]=a,a}_getContext(t,e,s,r){let n=this._contexts[this._contextId(t,e,s,r)];return n!==void 0?n:this._createContext(t,e,s,r)}_resetContext(t,e,s,r){delete this._contexts[this._contextId(t,e,s,r)]}_resetAllContexts(){this._contexts={}}decodeRect(t,e,s,r,n,a,h){if(n.rQwait("h264 header",8))return!1;let l=n.rQshift32(),x=n.rQshift32();if(n.rQwait("h264 payload",l,8))return!1;x&2?this._resetAllContexts():x&1&&this._resetContext(t,e,s,r);let u=this._getContext(t,e,s,r);if(u.lastUsed=this._tick++,l!==0){let d=n.rQshiftBytes(l,!1),X=u.decode(d);X!==null&&a.videoFrame(t,e,s,r,X)}return!0}};var ba=3,ma="rgb(40, 40, 40)",Br=17,Dr=50,Qr=19,Mt=75,Ke=50,wa=1e3,va=50,Ir=1,hi=2,Pr=6,Hr=16,Yi=19,Ur=22,Nr=30,Or=113,zr=129,Gr=256,de=1;var Vr=1<<24,li=1<<25,Wr=1<<26,Rt=1<<27,ci=1<<28,se=class i extends Ee{constructor(t,e,s){if(!t)throw new Error("Must specify target");if(!e)throw new Error("Must specify URL, WebSocket or RTCDataChannel");window.isSecureContext||U("noVNC requires a secure context (TLS). Expect crashes!"),super(),this._target=t,typeof e=="string"?this._url=e:(this._url=null,this._rawChannel=e),s=s||{},this._rfbCredentials=s.credentials||{},this._shared="shared"in s?!!s.shared:!0,this._repeaterID=s.repeaterID||"",this._wsProtocols=s.wsProtocols||[],this._rfbConnectionState="",this._rfbInitState="",this._rfbAuthScheme=-1,this._rfbCleanDisconnect=!0,this._rfbRSAAESAuthenticationState=null,this._rfbVersion=0,this._rfbMaxVersion=3.8,this._rfbTightVNC=!1,this._rfbVeNCryptState=0,this._rfbXvpVer=0,this._fbWidth=0,this._fbHeight=0,this._fbName="",this._capabilities={power:!1},this._supportsFence=!1,this._supportsContinuousUpdates=!1,this._enabledContinuousUpdates=!1,this._supportsSetDesktopSize=!1,this._screenID=0,this._screenFlags=0,this._pendingRemoteResize=!1,this._lastResize=0,this._qemuExtKeyEventSupported=!1,this._extendedPointerEventSupported=!1,this._clipboardText=null,this._clipboardServerCapabilitiesActions={},this._clipboardServerCapabilitiesFormats={},this._sock=null,this._display=null,this._flushing=!1,this._keyboard=null,this._gestures=null,this._resizeObserver=null,this._disconnTimer=null,this._resizeTimeout=null,this._mouseMoveTimer=null,this._decoders={},this._FBU={rects:0,x:0,y:0,width:0,height:0,encoding:null},this._mousePos={},this._mouseButtonMask=0,this._mouseLastMoveTime=0,this._viewportDragging=!1,this._viewportDragPos={},this._viewportHasMoved=!1,this._accumulatedWheelDeltaX=0,this._accumulatedWheelDeltaY=0,this._gestureLastTapTime=null,this._gestureFirstDoubleTapEv=null,this._gestureLastMagnitudeX=0,this._gestureLastMagnitudeY=0,this._eventHandlers={focusCanvas:this._focusCanvas.bind(this),handleResize:this._handleResize.bind(this),handleMouse:this._handleMouse.bind(this),handleWheel:this._handleWheel.bind(this),handleGesture:this._handleGesture.bind(this),handleRSAAESCredentialsRequired:this._handleRSAAESCredentialsRequired.bind(this),handleRSAAESServerVerification:this._handleRSAAESServerVerification.bind(this)},L(">> RFB.constructor"),this._screen=document.createElement("div"),this._screen.style.display="flex",this._screen.style.width="100%",this._screen.style.height="100%",this._screen.style.overflow="auto",this._screen.style.background=ma,this._canvas=document.createElement("canvas"),this._canvas.style.margin="auto",this._canvas.style.outline="none",this._canvas.width=0,this._canvas.height=0,this._canvas.tabIndex=-1,this._screen.appendChild(this._canvas),this._cursor=new vt,this._cursorImage=i.cursors.none,this._decoders[R.encodingRaw]=new Kt,this._decoders[R.encodingCopyRect]=new Ft,this._decoders[R.encodingRRE]=new kt,this._decoders[R.encodingHextile]=new St,this._decoders[R.encodingZlib]=new Et,this._decoders[R.encodingTight]=new Qe,this._decoders[R.encodingTightPNG]=new At,this._decoders[R.encodingZRLE]=new Ct,this._decoders[R.encodingJPEG]=new Lt,this._decoders[R.encodingH264]=new Tt;try{this._display=new rt(this._canvas)}catch(r){throw U("Display exception: "+r),r}this._keyboard=new bt(this._canvas),this._keyboard.onkeyevent=this._handleKeyEvent.bind(this),this._remoteCapsLock=null,this._remoteNumLock=null,this._gestures=new mt,this._sock=new yt,this._sock.on("open",this._socketOpen.bind(this)),this._sock.on("close",this._socketClose.bind(this)),this._sock.on("message",this._handleMessage.bind(this)),this._sock.on("error",this._socketError.bind(this)),this._expectedClientWidth=null,this._expectedClientHeight=null,this._resizeObserver=new ResizeObserver(this._eventHandlers.handleResize),this._updateConnectionState("connecting"),L("<< RFB.constructor"),this.dragViewport=!1,this.focusOnClick=!0,this._viewOnly=!1,this._clipViewport=!1,this._clippingViewport=!1,this._scaleViewport=!1,this._resizeSession=!1,this._showDotCursor=!1,s.showDotCursor!==void 0&&(W("Specifying showDotCursor as a RFB constructor argument is deprecated"),this._showDotCursor=s.showDotCursor),this._qualityLevel=6,this._compressionLevel=2}get viewOnly(){return this._viewOnly}set viewOnly(t){this._viewOnly=t,(this._rfbConnectionState==="connecting"||this._rfbConnectionState==="connected")&&(t?this._keyboard.ungrab():this._keyboard.grab())}get capabilities(){return this._capabilities}get clippingViewport(){return this._clippingViewport}_setClippingViewport(t){t!==this._clippingViewport&&(this._clippingViewport=t,this.dispatchEvent(new CustomEvent("clippingviewport",{detail:this._clippingViewport})))}get touchButton(){return 0}set touchButton(t){W("Using old API!")}get clipViewport(){return this._clipViewport}set clipViewport(t){this._clipViewport=t,this._updateClip()}get scaleViewport(){return this._scaleViewport}set scaleViewport(t){this._scaleViewport=t,t&&this._clipViewport&&this._updateClip(),this._updateScale(),!t&&this._clipViewport&&this._updateClip()}get resizeSession(){return this._resizeSession}set resizeSession(t){this._resizeSession=t,t&&this._requestRemoteResize()}get showDotCursor(){return this._showDotCursor}set showDotCursor(t){this._showDotCursor=t,this._refreshCursor()}get background(){return this._screen.style.background}set background(t){this._screen.style.background=t}get qualityLevel(){return this._qualityLevel}set qualityLevel(t){if(!Number.isInteger(t)||t<0||t>9){U("qualityLevel must be an integer between 0 and 9");return}this._qualityLevel!==t&&(this._qualityLevel=t,this._rfbConnectionState==="connected"&&this._sendEncodings())}get compressionLevel(){return this._compressionLevel}set compressionLevel(t){if(!Number.isInteger(t)||t<0||t>9){U("compressionLevel must be an integer between 0 and 9");return}this._compressionLevel!==t&&(this._compressionLevel=t,this._rfbConnectionState==="connected"&&this._sendEncodings())}disconnect(){this._updateConnectionState("disconnecting"),this._sock.off("error"),this._sock.off("message"),this._sock.off("open"),this._rfbRSAAESAuthenticationState!==null&&this._rfbRSAAESAuthenticationState.disconnect()}approveServer(){this._rfbRSAAESAuthenticationState!==null&&this._rfbRSAAESAuthenticationState.approveServer()}sendCredentials(t){this._rfbCredentials=t,this._resumeAuthentication()}sendCtrlAltDel(){this._rfbConnectionState!=="connected"||this._viewOnly||(V("Sending Ctrl-Alt-Del"),this.sendKey(f.XK_Control_L,"ControlLeft",!0),this.sendKey(f.XK_Alt_L,"AltLeft",!0),this.sendKey(f.XK_Delete,"Delete",!0),this.sendKey(f.XK_Delete,"Delete",!1),this.sendKey(f.XK_Alt_L,"AltLeft",!1),this.sendKey(f.XK_Control_L,"ControlLeft",!1))}machineShutdown(){this._xvpOp(1,2)}machineReboot(){this._xvpOp(1,3)}machineReset(){this._xvpOp(1,4)}sendKey(t,e,s){if(this._rfbConnectionState!=="connected"||this._viewOnly)return;if(s===void 0){this.sendKey(t,e,!0),this.sendKey(t,e,!1);return}let r=Kr[e];if(this._qemuExtKeyEventSupported&&r)t=t||0,V("Sending key ("+(s?"down":"up")+"): keysym "+t+", scancode "+r),i.messages.QEMUExtendedKeyEvent(this._sock,t,s,r);else{if(!t)return;V("Sending keysym ("+(s?"down":"up")+"): "+t),i.messages.keyEvent(this._sock,t,s?1:0)}}focus(t){this._canvas.focus(t)}blur(){this._canvas.blur()}clipboardPasteFrom(t){if(!(this._rfbConnectionState!=="connected"||this._viewOnly))if(this._clipboardServerCapabilitiesFormats[de]&&this._clipboardServerCapabilitiesActions[Rt])this._clipboardText=t,i.messages.extendedClipboardNotify(this._sock,[de]);else{let e,s,r;e=0;for(let n of t)e++;r=new Uint8Array(e),s=0;for(let n of t){let a=n.codePointAt(0);a>255&&(a=63),r[s++]=a}i.messages.clientCutText(this._sock,r)}}getImageData(){return this._display.getImageData()}toDataURL(t,e){return this._display.toDataURL(t,e)}toBlob(t,e,s){return this._display.toBlob(t,e,s)}_connect(){if(L(">> RFB.connect"),this._url)V(`connecting to ${this._url}`),this._sock.open(this._url,this._wsProtocols);else{if(V(`attaching ${this._rawChannel} to Websock`),this._sock.attach(this._rawChannel),this._sock.readyState==="closed")throw Error("Cannot use already closed WebSocket/RTCDataChannel");this._sock.readyState==="open"&&this._socketOpen()}this._target.appendChild(this._screen),this._gestures.attach(this._canvas),this._cursor.attach(this._canvas),this._refreshCursor(),this._resizeObserver.observe(this._screen),this._canvas.addEventListener("mousedown",this._eventHandlers.focusCanvas),this._canvas.addEventListener("touchstart",this._eventHandlers.focusCanvas),this._canvas.addEventListener("mousedown",this._eventHandlers.handleMouse),this._canvas.addEventListener("mouseup",this._eventHandlers.handleMouse),this._canvas.addEventListener("mousemove",this._eventHandlers.handleMouse),this._canvas.addEventListener("click",this._eventHandlers.handleMouse),this._canvas.addEventListener("contextmenu",this._eventHandlers.handleMouse),this._canvas.addEventListener("wheel",this._eventHandlers.handleWheel),this._canvas.addEventListener("gesturestart",this._eventHandlers.handleGesture),this._canvas.addEventListener("gesturemove",this._eventHandlers.handleGesture),this._canvas.addEventListener("gestureend",this._eventHandlers.handleGesture),L("<< RFB.connect")}_disconnect(){L(">> RFB.disconnect"),this._cursor.detach(),this._canvas.removeEventListener("gesturestart",this._eventHandlers.handleGesture),this._canvas.removeEventListener("gesturemove",this._eventHandlers.handleGesture),this._canvas.removeEventListener("gestureend",this._eventHandlers.handleGesture),this._canvas.removeEventListener("wheel",this._eventHandlers.handleWheel),this._canvas.removeEventListener("mousedown",this._eventHandlers.handleMouse),this._canvas.removeEventListener("mouseup",this._eventHandlers.handleMouse),this._canvas.removeEventListener("mousemove",this._eventHandlers.handleMouse),this._canvas.removeEventListener("click",this._eventHandlers.handleMouse),this._canvas.removeEventListener("contextmenu",this._eventHandlers.handleMouse),this._canvas.removeEventListener("mousedown",this._eventHandlers.focusCanvas),this._canvas.removeEventListener("touchstart",this._eventHandlers.focusCanvas),this._resizeObserver.disconnect(),this._keyboard.ungrab(),this._gestures.detach(),this._sock.close();try{this._target.removeChild(this._screen)}catch(t){if(t.name!=="NotFoundError")throw t}clearTimeout(this._resizeTimeout),clearTimeout(this._mouseMoveTimer),L("<< RFB.disconnect")}_socketOpen(){this._rfbConnectionState==="connecting"&&this._rfbInitState===""?(this._rfbInitState="ProtocolVersion",L("Starting VNC handshake")):this._fail("Unexpected server connection while "+this._rfbConnectionState)}_socketClose(t){L("WebSocket on-close event");let e="";switch(t.code&&(e="(code: "+t.code,t.reason&&(e+=", reason: "+t.reason),e+=")"),this._rfbConnectionState){case"connecting":this._fail("Connection closed "+e);break;case"connected":this._updateConnectionState("disconnecting"),this._updateConnectionState("disconnected");break;case"disconnecting":this._updateConnectionState("disconnected");break;case"disconnected":this._fail("Unexpected server disconnect when already disconnected "+e);break;default:this._fail("Unexpected server disconnect before connecting "+e);break}this._sock.off("close"),this._rawChannel=null}_socketError(t){W("WebSocket on-error event")}_focusCanvas(t){this.focusOnClick&&this.focus({preventScroll:!0})}_setDesktopName(t){this._fbName=t,this.dispatchEvent(new CustomEvent("desktopname",{detail:{name:this._fbName}}))}_saveExpectedClientSize(){this._expectedClientWidth=this._screen.clientWidth,this._expectedClientHeight=this._screen.clientHeight}_currentClientSize(){return[this._screen.clientWidth,this._screen.clientHeight]}_clientHasExpectedSize(){let[t,e]=this._currentClientSize();return t==this._expectedClientWidth&&e==this._expectedClientHeight}_handleResize(){this._clientHasExpectedSize()||(window.requestAnimationFrame(()=>{this._updateClip(),this._updateScale(),this._saveExpectedClientSize()}),this._requestRemoteResize())}_updateClip(){let t=this._display.clipViewport,e=this._clipViewport;if(this._scaleViewport&&(e=!1),t!==e&&(this._display.clipViewport=e),e){let s=this._screenSize();this._display.viewportChangeSize(s.w,s.h),this._fixScrollbars(),this._setClippingViewport(s.w{U("Disconnection timed out."),this._updateConnectionState("disconnected")},ba*1e3);break;case"disconnected":this.dispatchEvent(new CustomEvent("disconnect",{detail:{clean:this._rfbCleanDisconnect}}));break}}_fail(t){switch(this._rfbConnectionState){case"disconnecting":U("Failed when disconnecting: "+t);break;case"connected":U("Failed while connected: "+t);break;case"connecting":U("Failed when connecting: "+t);break;default:U("RFB failure: "+t);break}return this._rfbCleanDisconnect=!1,this._updateConnectionState("disconnecting"),this._updateConnectionState("disconnected"),!1}_setCapability(t,e){this._capabilities[t]=e,this.dispatchEvent(new CustomEvent("capabilities",{detail:{capabilities:this._capabilities}}))}_handleMessage(){if(this._sock.rQwait("message",1)){W("handleMessage called on an empty receive queue");return}switch(this._rfbConnectionState){case"disconnected":U("Got data while disconnected");break;case"connected":for(;!(this._flushing||!this._normalMsg()||this._sock.rQwait("message",1)););break;case"connecting":for(;this._rfbConnectionState==="connecting"&&this._initMsg(););break;default:U("Got data while in an invalid state");break}}_handleKeyEvent(t,e,s,r,n){e=="CapsLock"&&s&&(this._remoteCapsLock=null),this._remoteCapsLock!==null&&n!==null&&this._remoteCapsLock!==n&&s&&(L("Fixing remote caps lock"),this.sendKey(f.XK_Caps_Lock,"CapsLock",!0),this.sendKey(f.XK_Caps_Lock,"CapsLock",!1),this._remoteCapsLock=null),e=="NumLock"&&s&&(this._remoteNumLock=null),this._remoteNumLock!==null&&r!==null&&this._remoteNumLock!==r&&s&&(L("Fixing remote num lock"),this.sendKey(f.XK_Num_Lock,"NumLock",!0),this.sendKey(f.XK_Num_Lock,"NumLock",!1),this._remoteNumLock=null),this.sendKey(t,e,s)}static _convertButtonMask(t){let e={0:1,1:4,2:2,3:128,4:256},s=0;for(let r=0;r<5;r++)t&1<st||Math.abs(a)>st)&&(this._viewportHasMoved=!0,this._viewportDragPos={x:e.x,y:e.y},this._display.viewportChangePos(n,a));break}this._handleMouseMove(e.x,e.y);break}}_handleMouseButton(t,e,s){this._flushMouseMoveTimer(t,e),this._mouseButtonMask=s,this._sendMouse(t,e,this._mouseButtonMask)}_handleMouseMove(t,e){if(this._mousePos={x:t,y:e},this._mouseMoveTimer==null){let s=Date.now()-this._mouseLastMoveTime;s>Br?(this._sendMouse(t,e,this._mouseButtonMask),this._mouseLastMoveTime=Date.now()):this._mouseMoveTimer=setTimeout(()=>{this._handleDelayedMouseMove()},Br-s)}}_handleDelayedMouseMove(){this._mouseMoveTimer=null,this._sendMouse(this._mousePos.x,this._mousePos.y,this._mouseButtonMask),this._mouseLastMoveTime=Date.now()}_sendMouse(t,e,s){if(this._rfbConnectionState!=="connected"||this._viewOnly)return;if(s&32768)throw new Error("Illegal mouse button mask (mask: "+s+")");let r=s&32640;this._extendedPointerEventSupported&&r?i.messages.extendedPointerEvent(this._sock,this._display.absX(t),this._display.absY(e),s):i.messages.pointerEvent(this._sock,this._display.absX(t),this._display.absY(e),s)}_handleWheel(t){if(this._rfbConnectionState!=="connected"||this._viewOnly)return;t.stopPropagation(),t.preventDefault();let e=Ne(t.clientX,t.clientY,this._canvas),s=i._convertButtonMask(t.buttons),r=t.deltaX,n=t.deltaY;t.deltaMode!==0&&(r*=Qr,n*=Qr),this._accumulatedWheelDeltaX+=r,this._accumulatedWheelDeltaY+=n,Math.abs(this._accumulatedWheelDeltaX)>=Dr&&(this._accumulatedWheelDeltaX<0?(this._handleMouseButton(e.x,e.y,s|32),this._handleMouseButton(e.x,e.y,s)):this._accumulatedWheelDeltaX>0&&(this._handleMouseButton(e.x,e.y,s|64),this._handleMouseButton(e.x,e.y,s)),this._accumulatedWheelDeltaX=0),Math.abs(this._accumulatedWheelDeltaY)>=Dr&&(this._accumulatedWheelDeltaY<0?(this._handleMouseButton(e.x,e.y,s|8),this._handleMouseButton(e.x,e.y,s)):this._accumulatedWheelDeltaY>0&&(this._handleMouseButton(e.x,e.y,s|16),this._handleMouseButton(e.x,e.y,s)),this._accumulatedWheelDeltaY=0)}_fakeMouseMove(t,e,s){this._handleMouseMove(e,s),this._cursor.move(t.detail.clientX,t.detail.clientY)}_handleTapEvent(t,e){let s=Ne(t.detail.clientX,t.detail.clientY,this._canvas);if(this._gestureLastTapTime!==null&&Date.now()-this._gestureLastTapTimest||Math.abs(n)>st)&&(this._viewportHasMoved=!0,this._viewportDragPos={x:s.x,y:s.y},this._display.viewportChangePos(r,n))}else this._fakeMouseMove(t,s.x,s.y);break;case"twodrag":for(this._fakeMouseMove(t,s.x,s.y);t.detail.magnitudeY-this._gestureLastMagnitudeY>Ke;)this._handleMouseButton(s.x,s.y,8),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeY+=Ke;for(;t.detail.magnitudeY-this._gestureLastMagnitudeY<-Ke;)this._handleMouseButton(s.x,s.y,16),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeY-=Ke;for(;t.detail.magnitudeX-this._gestureLastMagnitudeX>Ke;)this._handleMouseButton(s.x,s.y,32),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeX+=Ke;for(;t.detail.magnitudeX-this._gestureLastMagnitudeX<-Ke;)this._handleMouseButton(s.x,s.y,64),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeX-=Ke;break;case"pinch":if(this._fakeMouseMove(t,s.x,s.y),e=Math.hypot(t.detail.magnitudeX,t.detail.magnitudeY),Math.abs(e-this._gestureLastMagnitudeX)>Mt){for(this._handleKeyEvent(f.XK_Control_L,"ControlLeft",!0);e-this._gestureLastMagnitudeX>Mt;)this._handleMouseButton(s.x,s.y,8),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeX+=Mt;for(;e-this._gestureLastMagnitudeX<-Mt;)this._handleMouseButton(s.x,s.y,16),this._handleMouseButton(s.x,s.y,0),this._gestureLastMagnitudeX-=Mt}this._handleKeyEvent(f.XK_Control_L,"ControlLeft",!1);break}break;case"gestureend":switch(t.detail.type){case"onetap":case"twotap":case"threetap":case"pinch":case"twodrag":break;case"drag":this.dragViewport?this._viewportDragging=!1:(this._fakeMouseMove(t,s.x,s.y),this._handleMouseButton(s.x,s.y,0));break;case"longpress":if(this._viewportHasMoved)break;this.dragViewport&&!this._viewportHasMoved?(this._fakeMouseMove(t,s.x,s.y),this._handleMouseButton(s.x,s.y,4),this._handleMouseButton(s.x,s.y,0),this._viewportDragging=!1):(this._fakeMouseMove(t,s.x,s.y),this._handleMouseButton(s.x,s.y,0));break}break}}_flushMouseMoveTimer(t,e){this._mouseMoveTimer!==null&&(clearTimeout(this._mouseMoveTimer),this._mouseMoveTimer=null,this._sendMouse(t,e,this._mouseButtonMask))}_negotiateProtocolVersion(){if(this._sock.rQwait("version",12))return!1;let t=this._sock.rQshiftStr(12).substr(4,7);V("Server ProtocolVersion: "+t);let e=0;switch(t){case"000.000":e=1;break;case"003.003":case"003.006":this._rfbVersion=3.3;break;case"003.007":this._rfbVersion=3.7;break;case"003.008":case"003.889":case"004.000":case"004.001":case"005.000":this._rfbVersion=3.8;break;default:return this._fail("Invalid server version "+t)}if(e){let r="ID:"+this._repeaterID;for(;r.length<250;)r+="\0";return this._sock.sQpushString(r),this._sock.flush(),!0}this._rfbVersion>this._rfbMaxVersion&&(this._rfbVersion=this._rfbMaxVersion);let s="00"+parseInt(this._rfbVersion,10)+".00"+this._rfbVersion*10%10;this._sock.sQpushString("RFB "+s+`
+`),this._sock.flush(),L("Sent ProtocolVersion: "+s),this._rfbInitState="Security"}_isSupportedSecurityType(t){return[Ir,hi,Pr,Hr,Yi,Ur,Nr,Or,Gr].includes(t)}_negotiateSecurity(){if(this._rfbVersion>=3.7){let t=this._sock.rQshift8();if(this._sock.rQwait("security type",t,1))return!1;if(t===0)return this._rfbInitState="SecurityReason",this._securityContext="no security types",this._securityStatus=1,!0;let e=this._sock.rQshiftBytes(t);L("Server security types: "+e),this._rfbAuthScheme=-1;for(let s of e)if(this._isSupportedSecurityType(s)){this._rfbAuthScheme=s;break}if(this._rfbAuthScheme===-1)return this._fail("Unsupported security types (types: "+e+")");this._sock.sQpush8(this._rfbAuthScheme),this._sock.flush()}else{if(this._sock.rQwait("security scheme",4))return!1;if(this._rfbAuthScheme=this._sock.rQshift32(),this._rfbAuthScheme==0)return this._rfbInitState="SecurityReason",this._securityContext="authentication scheme",this._securityStatus=1,!0}return this._rfbInitState="Authentication",L("Authenticating using scheme: "+this._rfbAuthScheme),!0}_handleSecurityReason(){if(this._sock.rQwait("reason length",4))return!1;let t=this._sock.rQshift32(),e="";if(t>0){if(this._sock.rQwait("reason",t,4))return!1;e=this._sock.rQshiftStr(t)}return e!==""?(this.dispatchEvent(new CustomEvent("securityfailure",{detail:{status:this._securityStatus,reason:e}})),this._fail("Security negotiation failed on "+this._securityContext+" (reason: "+e+")")):(this.dispatchEvent(new CustomEvent("securityfailure",{detail:{status:this._securityStatus}})),this._fail("Security negotiation failed on "+this._securityContext))}_negotiateXvpAuth(){return this._rfbCredentials.username===void 0||this._rfbCredentials.password===void 0||this._rfbCredentials.target===void 0?(this.dispatchEvent(new CustomEvent("credentialsrequired",{detail:{types:["username","password","target"]}})),!1):(this._sock.sQpush8(this._rfbCredentials.username.length),this._sock.sQpush8(this._rfbCredentials.target.length),this._sock.sQpushString(this._rfbCredentials.username),this._sock.sQpushString(this._rfbCredentials.target),this._sock.flush(),this._rfbAuthScheme=hi,this._negotiateAuthentication())}_negotiateVeNCryptAuth(){if(this._rfbVeNCryptState==0){if(this._sock.rQwait("vencrypt version",2))return!1;let t=this._sock.rQshift8(),e=this._sock.rQshift8();if(!(t==0&&e==2))return this._fail("Unsupported VeNCrypt version "+t+"."+e);this._sock.sQpush8(0),this._sock.sQpush8(2),this._sock.flush(),this._rfbVeNCryptState=1}if(this._rfbVeNCryptState==1){if(this._sock.rQwait("vencrypt ack",1))return!1;let t=this._sock.rQshift8();if(t!=0)return this._fail("VeNCrypt failure "+t);this._rfbVeNCryptState=2}if(this._rfbVeNCryptState==2){if(this._sock.rQwait("vencrypt subtypes length",1))return!1;let t=this._sock.rQshift8();if(t<1)return this._fail("VeNCrypt subtypes empty");this._rfbVeNCryptSubtypesLength=t,this._rfbVeNCryptState=3}if(this._rfbVeNCryptState==3){if(this._sock.rQwait("vencrypt subtypes",4*this._rfbVeNCryptSubtypesLength))return!1;let t=[];for(let e=0;e0&&this._sock.rQwait("tunnel capabilities",16*r,4))return!1;if(this._rfbTightVNC=!0,r>0)return this._negotiateTightTunnels(r),!1}if(this._sock.rQwait("sub auth count",4))return!1;let t=this._sock.rQshift32();if(t===0)return this._rfbInitState="SecurityResult",!0;if(this._sock.rQwait("sub auth capabilities",16*t,4))return!1;let e={STDVNOAUTH__:1,STDVVNCAUTH_:2,TGHTULGNAUTH:129},s=[];for(let r=0;rthis._rfbCredentials),this._rfbRSAAESAuthenticationState.addEventListener("serververification",this._eventHandlers.handleRSAAESServerVerification),this._rfbRSAAESAuthenticationState.addEventListener("credentialsrequired",this._eventHandlers.handleRSAAESCredentialsRequired)),this._rfbRSAAESAuthenticationState.checkInternalEvents(),this._rfbRSAAESAuthenticationState.hasStarted||this._rfbRSAAESAuthenticationState.negotiateRA2neAuthAsync().catch(t=>{t.message!=="disconnect normally"&&this._fail(t.message)}).then(()=>(this._rfbInitState="SecurityResult",!0)).finally(()=>{this._rfbRSAAESAuthenticationState.removeEventListener("serververification",this._eventHandlers.handleRSAAESServerVerification),this._rfbRSAAESAuthenticationState.removeEventListener("credentialsrequired",this._eventHandlers.handleRSAAESCredentialsRequired),this._rfbRSAAESAuthenticationState=null}),!1}_negotiateMSLogonIIAuth(){if(this._sock.rQwait("mslogonii dh param",24))return!1;if(this._rfbCredentials.username===void 0||this._rfbCredentials.password===void 0)return this.dispatchEvent(new CustomEvent("credentialsrequired",{detail:{types:["username","password"]}})),!1;let t=this._sock.rQshiftBytes(8),e=this._sock.rQshiftBytes(8),s=this._sock.rQshiftBytes(8),r=O.generateKey({name:"DH",g:t,p:e},!0,["deriveBits"]),n=O.exportKey("raw",r.publicKey),a=O.deriveBits({name:"DH",public:s},r.privateKey,64),h=O.importKey("raw",a,{name:"DES-CBC"},!1,["encrypt"]),_=he(this._rfbCredentials.username).substring(0,255),o=he(this._rfbCredentials.password).substring(0,63),l=new Uint8Array(256),x=new Uint8Array(64);window.crypto.getRandomValues(l),window.crypto.getRandomValues(x);for(let u=0;u<_.length;u++)l[u]=_.charCodeAt(u);l[_.length]=0;for(let u=0;u=3.8?this._rfbInitState="SecurityResult":this._rfbInitState="ClientInitialisation",!0;case Ur:return this._negotiateXvpAuth();case Nr:return this._negotiateARDAuth();case hi:return this._negotiateStdVNCAuth();case Hr:return this._negotiateTightAuth();case Yi:return this._negotiateVeNCryptAuth();case Gr:return this._negotiatePlainAuth();case zr:return this._negotiateTightUnixAuth();case Pr:return this._negotiateRA2neAuth();case Or:return this._negotiateMSLogonIIAuth();default:return this._fail("Unsupported auth scheme (scheme: "+this._rfbAuthScheme+")")}}_handleSecurityResult(){if(this._sock.rQwait("VNC auth response ",4))return!1;let t=this._sock.rQshift32();return t===0?(this._rfbInitState="ClientInitialisation",L("Authentication OK"),!0):this._rfbVersion>=3.8?(this._rfbInitState="SecurityReason",this._securityContext="security result",this._securityStatus=t,!0):(this.dispatchEvent(new CustomEvent("securityfailure",{detail:{status:t}})),this._fail("Security handshake failed"))}_negotiateServerInit(){if(this._sock.rQwait("server initialization",24))return!1;let t=this._sock.rQshift16(),e=this._sock.rQshift16(),s=this._sock.rQshift8(),r=this._sock.rQshift8(),n=this._sock.rQshift8(),a=this._sock.rQshift8(),h=this._sock.rQshift16(),_=this._sock.rQshift16(),o=this._sock.rQshift16(),l=this._sock.rQshift8(),x=this._sock.rQshift8(),u=this._sock.rQshift8();this._sock.rQskipBytes(3);let d=this._sock.rQshift32();if(this._sock.rQwait("server init name",d,24))return!1;let X=this._sock.rQshiftStr(d);if(X=Bt(X,!0),this._rfbTightVNC){if(this._sock.rQwait("TightVNC extended server init header",8,24+d))return!1;let F=this._sock.rQshift16(),A=this._sock.rQshift16(),k=this._sock.rQshift16();this._sock.rQskipBytes(2);let T=(F+A+k)*16;if(this._sock.rQwait("TightVNC extended server init header",T,32+d))return!1;this._sock.rQskipBytes(16*F),this._sock.rQskipBytes(16*A),this._sock.rQskipBytes(16*k)}return V("Screen: "+t+"x"+e+", bpp: "+s+", depth: "+r+", bigEndian: "+n+", trueColor: "+a+", redMax: "+h+", greenMax: "+_+", blueMax: "+o+", redShift: "+l+", greenShift: "+x+", blueShift: "+u),this._setDesktopName(X),this._resize(t,e),this._viewOnly||this._keyboard.grab(),this._fbDepth=24,this._fbName==="Intel(r) AMT KVM"&&(W("Intel AMT KVM only supports 8/16 bit depths. Using low color mode."),this._fbDepth=8),i.messages.pixelFormat(this._sock,this._fbDepth,!0),this._sendEncodings(),i.messages.fbUpdateRequest(this._sock,!1,0,0,this._fbWidth,this._fbHeight),this._updateConnectionState("connected"),!0}_sendEncodings(){let t=[];t.push(R.encodingCopyRect),this._fbDepth==24&&(fi&&t.push(R.encodingH264),t.push(R.encodingTight),t.push(R.encodingTightPNG),t.push(R.encodingZRLE),t.push(R.encodingJPEG),t.push(R.encodingHextile),t.push(R.encodingRRE),t.push(R.encodingZlib)),t.push(R.encodingRaw),t.push(R.pseudoEncodingQualityLevel0+this._qualityLevel),t.push(R.pseudoEncodingCompressLevel0+this._compressionLevel),t.push(R.pseudoEncodingDesktopSize),t.push(R.pseudoEncodingLastRect),t.push(R.pseudoEncodingQEMUExtendedKeyEvent),t.push(R.pseudoEncodingQEMULedEvent),t.push(R.pseudoEncodingExtendedDesktopSize),t.push(R.pseudoEncodingXvp),t.push(R.pseudoEncodingFence),t.push(R.pseudoEncodingContinuousUpdates),t.push(R.pseudoEncodingDesktopName),t.push(R.pseudoEncodingExtendedClipboard),t.push(R.pseudoEncodingExtendedMouseButtons),this._fbDepth==24&&(t.push(R.pseudoEncodingVMwareCursor),t.push(R.pseudoEncodingCursor)),i.messages.clientEncodings(this._sock,t)}_initMsg(){switch(this._rfbInitState){case"ProtocolVersion":return this._negotiateProtocolVersion();case"Security":return this._negotiateSecurity();case"Authentication":return this._negotiateAuthentication();case"SecurityResult":return this._handleSecurityResult();case"SecurityReason":return this._handleSecurityReason();case"ClientInitialisation":return this._sock.sQpush8(this._shared?1:0),this._sock.flush(),this._rfbInitState="ServerInitialisation",!0;case"ServerInitialisation":return this._negotiateServerInit();default:return this._fail("Unknown init state (state: "+this._rfbInitState+")")}}_resumeAuthentication(){setTimeout(this._initMsg.bind(this),0)}_handleSetColourMapMsg(){return L("SetColorMapEntries"),this._fail("Unexpected SetColorMapEntries message")}_handleServerCutText(){if(L("ServerCutText"),this._sock.rQwait("ServerCutText header",7,1))return!1;this._sock.rQskipBytes(3);let t=this._sock.rQshift32();if(t=it(t),this._sock.rQwait("ServerCutText content",Math.abs(t),8))return!1;if(t>=0){let e=this._sock.rQshiftStr(t);if(this._viewOnly)return!0;this.dispatchEvent(new CustomEvent("clipboard",{detail:{text:e}}))}else{t=Math.abs(t);let e=this._sock.rQshift32(),s=e&65535,r=e&4278190080;if(!!(r&Vr)){this._clipboardServerCapabilitiesFormats={},this._clipboardServerCapabilitiesActions={};for(let h=0;h<=15;h++){let _=1<0&&_.charAt(_.length-1)==="\0"&&(_=_.slice(0,-1)),_=_.replaceAll(`\r
+`,`
+`),this.dispatchEvent(new CustomEvent("clipboard",{detail:{text:_}}))}}else return this._fail("Unexpected action in extended clipboard message: "+r)}return!0}_handleServerFenceMsg(){if(this._sock.rQwait("ServerFence header",8,1))return!1;this._sock.rQskipBytes(3);let t=this._sock.rQshift32(),e=this._sock.rQshift8();if(this._sock.rQwait("ServerFence payload",e,9))return!1;e>64&&(W("Bad payload length ("+e+") in fence response"),e=64);let s=this._sock.rQshiftStr(e);return this._supportsFence=!0,t&1<<31?(t&=3,i.messages.clientFence(this._sock,t,s),!0):this._fail("Unexpected fence response")}_handleXvpMsg(){if(this._sock.rQwait("XVP version and message",3,1))return!1;this._sock.rQskipBytes(1);let t=this._sock.rQshift8(),e=this._sock.rQshift8();switch(e){case 0:U("XVP operation failed");break;case 1:this._rfbXvpVer=t,V("XVP extensions enabled (version "+this._rfbXvpVer+")"),this._setCapability("power",!0);break;default:this._fail("Illegal server XVP message (msg: "+e+")");break}return!0}_normalMsg(){let t;this._FBU.rects>0?t=0:t=this._sock.rQshift8();let e,s;switch(t){case 0:return s=this._framebufferUpdate(),s&&!this._enabledContinuousUpdates&&i.messages.fbUpdateRequest(this._sock,!0,0,0,this._fbWidth,this._fbHeight),s;case 1:return this._handleSetColourMapMsg();case 2:return L("Bell"),this.dispatchEvent(new CustomEvent("bell",{detail:{}})),!0;case 3:return this._handleServerCutText();case 150:return e=!this._supportsContinuousUpdates,this._supportsContinuousUpdates=!0,this._enabledContinuousUpdates=!1,e&&(this._enabledContinuousUpdates=!0,this._updateContinuousUpdates(),V("Enabling continuous updates.")),!0;case 248:return this._handleServerFenceMsg();case 250:return this._handleXvpMsg();default:return this._fail("Unexpected server message (type "+t+")"),L("sock.rQpeekBytes(30): "+this._sock.rQpeekBytes(30)),!0}}_framebufferUpdate(){if(this._FBU.rects===0){if(this._sock.rQwait("FBU header",3,1))return!1;if(this._sock.rQskipBytes(1),this._FBU.rects=this._sock.rQshift16(),this._display.pending())return this._flushing=!0,this._display.flush().then(()=>{this._flushing=!1,this._sock.rQwait("message",1)||this._handleMessage()}),!1}for(;this._FBU.rects>0;){if(this._FBU.encoding===null){if(this._sock.rQwait("rect header",12))return!1;this._FBU.x=this._sock.rQshift16(),this._FBU.y=this._sock.rQshift16(),this._FBU.width=this._sock.rQshift16(),this._FBU.height=this._sock.rQshift16(),this._FBU.encoding=this._sock.rQshift32(),this._FBU.encoding>>=0}if(!this._handleRect())return!1;this._FBU.rects--,this._FBU.encoding=null}return this._display.flip(),!0}_handleRect(){switch(this._FBU.encoding){case R.pseudoEncodingLastRect:return this._FBU.rects=1,!0;case R.pseudoEncodingVMwareCursor:return this._handleVMwareCursor();case R.pseudoEncodingCursor:return this._handleCursor();case R.pseudoEncodingQEMUExtendedKeyEvent:return this._qemuExtKeyEventSupported=!0,!0;case R.pseudoEncodingDesktopName:return this._handleDesktopName();case R.pseudoEncodingDesktopSize:return this._resize(this._FBU.width,this._FBU.height),!0;case R.pseudoEncodingExtendedDesktopSize:return this._handleExtendedDesktopSize();case R.pseudoEncodingExtendedMouseButtons:return this._extendedPointerEventSupported=!0,!0;case R.pseudoEncodingQEMULedEvent:return this._handleLedEvent();default:return this._handleDataRect()}}_handleVMwareCursor(){let t=this._FBU.x,e=this._FBU.y,s=this._FBU.width,r=this._FBU.height;if(this._sock.rQwait("VMware cursor encoding",1))return!1;let n=this._sock.rQshift8();this._sock.rQshift8();let a,h=4;if(n==0){if(a=new Array(s*r*h),this._sock.rQwait("VMware cursor classic encoding",s*r*h*2,2))return!1;let o=new Array(s*r);for(let x=0;x>8&255,X=u>>16&255,F=u>>24&255;a[x*h]=d,a[x*h+1]=X,a[x*h+2]=F,a[x*h+3]=255}else(o[x]&-256)==-256?l[x]==0?(a[x*h]=0,a[x*h+1]=0,a[x*h+2]=0,a[x*h+3]=0):(l[x]&-256,a[x*h]=0,a[x*h+1]=0,a[x*h+2]=0,a[x*h+3]=255):(a[x*h]=0,a[x*h+1]=0,a[x*h+2]=0,a[x*h+3]=255)}else if(n==1){if(this._sock.rQwait("VMware cursor alpha encoding",s*r*4,2))return!1;a=new Array(s*r*h);for(let _=0;_>24&255,a[_*4+1]=o>>16&255,a[_*4+2]=o>>8&255,a[_*4+3]=o&255}}else return W("The given cursor type is not supported: "+n+" given."),!1;return this._updateCursor(a,t,e,s,r),!0}_handleCursor(){let t=this._FBU.x,e=this._FBU.y,s=this._FBU.width,r=this._FBU.height,n=s*r*4,a=Math.ceil(s/8)*r,h=n+a;if(this._sock.rQwait("cursor encoding",h))return!1;let _=this._sock.rQshiftBytes(n),o=this._sock.rQshiftBytes(a),l=new Uint8Array(s*r*4),x=0;for(let u=0;un.charCodeAt(0)),r=O.importKey("raw",s,{name:"DES-ECB"},!1,["encrypt"]);return O.encrypt({name:"DES-ECB"},r,e)}};se.messages={keyEvent(i,t,e){i.sQpush8(4),i.sQpush8(e),i.sQpush16(0),i.sQpush32(t),i.flush()},QEMUExtendedKeyEvent(i,t,e,s){function r(a){let h=s>>8,_=s&255;return h===224&&_<127?_|128:a}i.sQpush8(255),i.sQpush8(0),i.sQpush16(e),i.sQpush32(t);let n=r(s);i.sQpush32(n),i.flush()},pointerEvent(i,t,e,s){i.sQpush8(5),s=s&127,i.sQpush8(s),i.sQpush16(t),i.sQpush16(e),i.flush()},extendedPointerEvent(i,t,e,s){i.sQpush8(5);let r=s>>7&255;if(r&252)throw new Error("Invalid mouse button mask: "+s);let n=s&127;n|=128,i.sQpush8(n),i.sQpush16(t),i.sQpush16(e),i.sQpush8(r),i.flush()},_buildExtendedClipboardFlags(i,t){let e=new Uint8Array(4),s=0,r=0;for(let n=0;n>24,e[1]=0,e[2]=0,e[3]=s,e},extendedClipboardProvide(i,t,e){let s=new gt,r=[];for(let h=0;h>24&255,_.length>>16&255,_.length>>8&255,_.length&255);for(let o=0;o<_.length;o++)r.push(_.charCodeAt(o))}let n=s.deflate(new Uint8Array(r)),a=new Uint8Array(4+n.length);a.set(se.messages._buildExtendedClipboardFlags([ci],t)),a.set(n,4),se.messages.clientCutText(i,a,!0)},extendedClipboardNotify(i,t){let e=se.messages._buildExtendedClipboardFlags([Rt],t);se.messages.clientCutText(i,e,!0)},extendedClipboardRequest(i,t){let e=se.messages._buildExtendedClipboardFlags([li],t);se.messages.clientCutText(i,e,!0)},extendedClipboardCaps(i,t,e){let s=Object.keys(e),r=new Uint8Array(4+4*s.length);s.map(a=>parseInt(a)),s.sort((a,h)=>a-h),r.set(se.messages._buildExtendedClipboardFlags(t,[]));let n=4;for(let a=0;a>24,r[n+1]=e[s[a]]>>16,r[n+2]=e[s[a]]>>8,r[n+3]=e[s[a]]>>0,n+=4,r[3]|=1<16?s=32:t>8?s=16:s=8;let r=Math.floor(t/3);i.sQpush8(0),i.sQpush8(0),i.sQpush8(0),i.sQpush8(0),i.sQpush8(s),i.sQpush8(t),i.sQpush8(0),i.sQpush8(e?1:0),i.sQpush16((1<"u"&&(e=0),typeof s>"u"&&(s=0),i.sQpush8(3),i.sQpush8(t?1:0),i.sQpush16(e),i.sQpush16(s),i.sQpush16(r),i.sQpush16(n),i.flush()},xvpOp(i,t,e){i.sQpush8(250),i.sQpush8(0),i.sQpush8(t),i.sQpush8(e),i.flush()}};se.cursors={none:{rgbaPixels:new Uint8Array,w:0,h:0,hotx:0,hoty:0},dot:{rgbaPixels:new Uint8Array([255,255,255,255,0,0,0,255,255,255,255,255,0,0,0,255,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,255,255,255,255,255]),w:3,h:3,hotx:1,hoty:1}};var Lo=se;export{Lo as default};
diff --git a/mobile/screen.html b/mobile/screen.html
new file mode 100644
index 0000000..9e482a3
--- /dev/null
+++ b/mobile/screen.html
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+Bot 的電腦
+
+
+
+
+
+
+
Bot 的電腦
連線中…
+
+
+
+
連線中…
+
+
+
+
+
+
+
+
diff --git a/relay/src/main.rs b/relay/src/main.rs
index 5ca309e..17a07e9 100644
--- a/relay/src/main.rs
+++ b/relay/src/main.rs
@@ -29,6 +29,9 @@ use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
const PAGE: &str = include_str!("../../mobile/index.html");
+/// The bots' computer, watched and used in a browser (noVNC).
+const SCREEN_PAGE: &str = include_str!("../../mobile/screen.html");
+const NOVNC: &str = include_str!("../../mobile/novnc.js");
const MANIFEST: &str = r##"{"name":"AnyBot","short_name":"AnyBot","start_url":"./","display":"standalone","background_color":"#121212","theme_color":"#121212","icons":[{"src":"icon.svg","sizes":"any","type":"image/svg+xml"},{"src":"icon-192.png","sizes":"192x192","type":"image/png","purpose":"any maskable"},{"src":"icon-512.png","sizes":"512x512","type":"image/png","purpose":"any maskable"}]}"##;
/// AnyBot's icon (the same drawing as the app's), and PNGs for the iPhone home screen and Android.
const ICON: &str = include_str!("../../mobile/icon.svg");
@@ -200,6 +203,10 @@ struct Relay {
/// The brain each AnyBot shows (when it is a screen of another), and which lend their hands.
uses: Mutex>,
hands: Mutex>,
+ /// People watching a bots' computer: stream id → (where the host's side goes, that host).
+ streams: Mutex>, String)>>,
+ /// One-time links for a browser to watch a host's computer: ticket → (account, host, made).
+ tickets: Mutex>,
/// 配對碼: code → (account, the AnyBot it is for, when made). Ten minutes, once.
pairs: Mutex>,
/// When each host last asked (in memory; saved now and then).
@@ -210,6 +217,32 @@ struct Relay {
contact: String,
}
+/// How long a link the app opens for a browser works (reconnects included).
+const TICKET_LIFE: Duration = Duration::from_secs(600);
+
+/// How long a connection's loop waits to read before sending what is queued: 2 ms while things
+/// move (a live screen), 40 ms when quiet. A fixed 40 ms added up to 40 ms to every message.
+struct Pace {
+ moved: Instant,
+ busy: Option,
+}
+
+impl Pace {
+ fn new() -> Pace {
+ Pace { moved: Instant::now() - Duration::from_secs(60), busy: None }
+ }
+ fn moved(&mut self) {
+ self.moved = Instant::now();
+ }
+ fn tune(&mut self, c: &std::net::TcpStream) {
+ let busy = self.moved.elapsed() < Duration::from_secs(2);
+ if self.busy != Some(busy) {
+ self.busy = Some(busy);
+ let _ = c.set_read_timeout(Some(Duration::from_millis(if busy { 2 } else { 40 })));
+ }
+ }
+}
+
fn now() -> i64 {
chrono::Utc::now().timestamp()
}
@@ -229,6 +262,8 @@ impl Relay {
flights: Mutex::new(HashMap::new()),
uses: Mutex::new(HashMap::new()),
hands: Mutex::new(std::collections::HashSet::new()),
+ streams: Mutex::new(HashMap::new()),
+ tickets: Mutex::new(HashMap::new()),
pairs: Mutex::new(HashMap::new()),
seen: Mutex::new(HashMap::new()),
fails: Mutex::new(HashMap::new()),
@@ -294,6 +329,95 @@ impl Relay {
e.1 = Instant::now();
}
+ /// A person's browser watching (and maybe using) an AnyBot's computer: its VNC stream goes
+ /// to that AnyBot over its lasting connection, and back. Signed in by the account session
+ /// (?t=, the phone page) or a ticket the account's app asked for (?ticket=); owners and
+ /// admins only.
+ fn vnc_viewer(self: &Arc, req: Req, mut c: std::net::TcpStream, host: &str) {
+ use std::io::Write;
+ use tungstenite::{protocol::Role, Message, WebSocket};
+ let user = match req.query.get("ticket").filter(|t| !t.is_empty()) {
+ Some(t) => self.tickets.lock().unwrap().get(t).filter(|(_, h, made)| h == host && made.elapsed() < TICKET_LIFE).map(|(u, _, _)| u.clone()),
+ None => self.session(&req),
+ };
+ let Some(user) = user else { return httpd::write_response(&mut c, &Resp::error(401, "請先登入")) };
+ let allowed = self.data.lock().unwrap().hosts.iter().any(|x| x.id == host && matches!(x.role_of(&user).as_deref(), Some("owner") | Some("admin")));
+ if !allowed {
+ return httpd::write_response(&mut c, &Resp::error(404, "找不到這台 AnyBot"));
+ }
+ let Some(to_host) = self.sockets.lock().unwrap().get(host).map(|s| s.tx.clone()) else {
+ return httpd::write_response(&mut c, &Resp::error(503, "這台 AnyBot 現在沒有連線"));
+ };
+ let key = req.header("sec-websocket-key").to_string();
+ if key.is_empty() {
+ return httpd::write_response(&mut c, &Resp::error(400, "bad request"));
+ }
+ let accept = tungstenite::handshake::derive_accept_key(key.as_bytes());
+ if c.write_all(format!("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {accept}\r\n\r\n").as_bytes()).is_err() {
+ return;
+ }
+ let _ = c.set_nodelay(true);
+ let mut ws = WebSocket::from_raw_socket(c, Role::Server, None);
+ let sid = httpd::random_hex(8);
+ let (tx, rx) = channel::>();
+ self.streams.lock().unwrap().insert(sid.clone(), (tx, host.to_string()));
+ if to_host.send(wire::pack(&json!({"t": "vopen", "sid": sid}), &[])).is_err() {
+ self.streams.lock().unwrap().remove(&sid);
+ return;
+ }
+ let (mut heard, mut pinged) = (Instant::now(), Instant::now());
+ let mut pace = Pace::new();
+ 'live: loop {
+ pace.tune(ws.get_ref());
+ let up = match ws.read() {
+ Ok(Message::Binary(b)) => Some(("vdata", b.to_vec())),
+ Ok(Message::Text(t)) => Some(("vtext", t.as_bytes().to_vec())),
+ Ok(Message::Close(_)) => break,
+ Ok(_) => None,
+ Err(tungstenite::Error::Io(e)) if matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => None,
+ Err(_) => break,
+ };
+ if let Some((t, b)) = up {
+ heard = Instant::now();
+ pace.moved();
+ if to_host.send(wire::pack(&json!({"t": t, "sid": sid}), &b)).is_err() {
+ break;
+ }
+ }
+ loop {
+ match rx.try_recv() {
+ Ok(b) => {
+ pace.moved();
+ if ws.write(Message::Binary(b.into())).is_err() {
+ break 'live;
+ }
+ }
+ Err(std::sync::mpsc::TryRecvError::Empty) => break,
+ // The host closed it (or went away).
+ Err(std::sync::mpsc::TryRecvError::Disconnected) => break 'live,
+ }
+ }
+ if pinged.elapsed().as_secs() >= WS_PING_SECS {
+ pinged = Instant::now();
+ if ws.write(Message::Ping(Vec::new().into())).is_err() {
+ break;
+ }
+ }
+ match ws.flush() {
+ Err(tungstenite::Error::Io(e)) if matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => {}
+ Err(_) => break,
+ Ok(()) => {}
+ }
+ if heard.elapsed().as_secs() >= WS_DEAD_SECS {
+ break;
+ }
+ }
+ let _ = ws.close(None);
+ let _ = ws.flush();
+ self.streams.lock().unwrap().remove(&sid);
+ let _ = to_host.send(wire::pack(&json!({"t": "vclose", "sid": sid}), &[]));
+ }
+
/// Hand a request to a host and wait for its answer.
fn forward(&self, host_id: &str, method: &str, path: &str, query: &HashMap, body: &[u8], who: (&str, &str)) -> Resp {
self.forward_raw(host_id, method, path, query, body, false, who, false, "").into_resp()
@@ -358,6 +482,10 @@ impl Relay {
fn ws(self: &Arc, req: Req, mut c: std::net::TcpStream) {
use std::io::Write;
use tungstenite::{protocol::Role, Message, WebSocket};
+ if let Some(host) = req.path.strip_prefix("/relay/vnc/") {
+ let host = host.to_string();
+ return self.vnc_viewer(req, c, &host);
+ }
if req.path != "/relay/host/ws" {
return httpd::write_response(&mut c, &Resp::error(404, "沒有這個網址"));
}
@@ -379,10 +507,13 @@ impl Relay {
self.sockets.lock().unwrap().insert(h.id.clone(), Sock { tx: tx.clone(), serial });
self.seen.lock().unwrap().insert(h.id.clone(), Instant::now());
let (mut heard, mut pinged, mut marked) = (Instant::now(), Instant::now(), Instant::now());
+ let mut pace = Pace::new();
'live: loop {
+ pace.tune(ws.get_ref());
match ws.read() {
Ok(Message::Binary(b)) => {
heard = Instant::now();
+ pace.moved();
self.on_frame(&h, &b, &tx);
}
Ok(Message::Close(_)) => break,
@@ -391,6 +522,7 @@ impl Relay {
Err(_) => break,
}
while let Ok(f) = rx.try_recv() {
+ pace.moved();
if ws.write(Message::Binary(f.into())).is_err() {
break 'live;
}
@@ -414,7 +546,8 @@ impl Relay {
self.seen.lock().unwrap().insert(h.id.clone(), Instant::now());
}
}
- // Gone: whoever waits on it hears so now, not after a timeout.
+ // Gone: its viewers' streams end, and whoever waits on it hears so now, not after a timeout.
+ self.streams.lock().unwrap().retain(|_, (_, host)| *host != h.id);
{
let mut socks = self.sockets.lock().unwrap();
if socks.get(&h.id).map(|s| s.serial) == Some(serial) {
@@ -434,6 +567,20 @@ impl Relay {
let Some((head, body)) = wire::unpack(b) else { return };
let s = |k: &str| head[k].as_str().unwrap_or("").to_string();
match head["t"].as_str().unwrap_or("") {
+ // A viewer's stream: only the host it was opened on may write to it.
+ "vdata" => {
+ if let Some((v, host)) = self.streams.lock().unwrap().get(&s("sid")) {
+ if *host == h.id {
+ let _ = v.send(body);
+ }
+ }
+ }
+ "vclose" => {
+ let mut all = self.streams.lock().unwrap();
+ if all.get(&s("sid")).map(|(_, host)| *host == h.id).unwrap_or(false) {
+ all.remove(&s("sid"));
+ }
+ }
"reply" => {
// Only the host the job went to may answer it.
let w = {
@@ -499,6 +646,25 @@ impl Relay {
("GET", []) | ("GET", ["index.html"]) => Resp::bytes("text/html; charset=utf-8", PAGE.as_bytes().to_vec())
.with_header("Content-Security-Policy", "default-src 'self'; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self'")
.with_header("Strict-Transport-Security", "max-age=31536000"),
+ ("GET", ["screen"]) => Resp::bytes("text/html; charset=utf-8", SCREEN_PAGE.as_bytes().to_vec())
+ .with_header("Content-Security-Policy", "default-src 'self'; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' wss: ws:")
+ .with_header("Strict-Transport-Security", "max-age=31536000"),
+ ("GET", ["novnc.js"]) => Resp::bytes("text/javascript; charset=utf-8", NOVNC.as_bytes().to_vec()).with_header("Cache-Control", "public, max-age=86400"),
+ // A link for this account's browser to watch one of its AnyBots' computer (the app
+ // opens it; the browser has no session of its own).
+ ("POST", ["relay", "screen-ticket"]) => {
+ let Some(h) = self.host(&req) else { return Resp::error(401, "這台電腦的金鑰無效") };
+ let want = req.json()["host"].as_str().unwrap_or("").to_string();
+ let ok = self.data.lock().unwrap().hosts.iter().any(|x| x.id == want && matches!(x.role_of(&h.user).as_deref(), Some("owner") | Some("admin")));
+ if !ok {
+ return Resp::error(404, "找不到這台 AnyBot");
+ }
+ let t = httpd::random_hex(24);
+ let mut all = self.tickets.lock().unwrap();
+ all.retain(|_, (_, _, made)| made.elapsed() < TICKET_LIFE);
+ all.insert(t.clone(), (h.user.clone(), want, Instant::now()));
+ Resp::json(&json!({"ticket": t}))
+ }
("GET", ["manifest.webmanifest"]) => Resp::bytes("application/manifest+json", MANIFEST.as_bytes().to_vec()),
("GET", ["icon.svg"]) => Resp::bytes("image/svg+xml", ICON.as_bytes().to_vec()),
("GET", [name]) if ICON_PNGS.iter().any(|(n, _)| n == name) => Resp::bytes("image/png", ICON_PNGS.iter().find(|(n, _)| n == name).map(|(_, b)| b.to_vec()).unwrap_or_default()),
diff --git a/src/app.rs b/src/app.rs
index 41c3f34..8a8658e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -371,13 +371,6 @@ impl App {
app.state.selected = 2;
app.show_routines = Some(2);
}
- Ok("viewer") => {
- crate::cloudbox::start_background();
- let _ = crate::cloudbox::ensure_running();
- app.cu.demo_live = true;
- app.cu.viewer_open = true;
- app.cu.fit = true;
- }
Ok("attach") => {
app.state.selected = 1;
let dir = std::env::temp_dir().join("anybot-shot-attach");
@@ -511,7 +504,6 @@ impl App {
// The real box, so the 電腦 card shows its screen.
crate::cloudbox::start_background();
let _ = crate::cloudbox::ensure_running();
- app.cu.demo_live = true;
app.state.selected = 1;
app.panel = Some(panel_ui::PanelTab::Activity);
}
@@ -3427,11 +3419,6 @@ impl App {
ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(vec2(w, h)));
}
}
- // Real data: the computer viewer opens a little after start, as a person would open it (a
- // screen of a brain has said so by then, so it shows the brain's computer).
- if self.frames == 10 && !self.demo && std::env::var("ANYBOT_SHOT_VIEW").as_deref() == Ok("viewer") {
- self.open_viewer();
- }
// ANYBOT_SHOT_PAGE=id: open that page late (a screen gets its pages from the brain first).
if self.frames == need * 3 / 4 {
if let Some(id) = std::env::var("ANYBOT_SHOT_PAGE").ok().and_then(|v| v.parse().ok()) {
@@ -3767,7 +3754,6 @@ impl eframe::App for App {
self.delete_dialog(ctx);
self.routines_window(ctx);
self.rebuild_dialog(ctx);
- self.viewer_window(ctx);
self.local_control_bar(ctx);
self.teach_bar(ctx);
self.approval_popup(ctx);
diff --git a/src/app/computers_ui.rs b/src/app/computers_ui.rs
index 794466b..9f78e72 100644
--- a/src/app/computers_ui.rs
+++ b/src/app/computers_ui.rs
@@ -7,9 +7,6 @@
use super::*;
use crate::cloudbox::{self, Rebuild, Run};
-/// While the viewer is open it redraws at least this often (new pictures ask for a redraw
-/// themselves as they arrive).
-const VIEWER_FRAME_MS: u64 = 450;
/// The floating bar stays this long after the bot's last mouse/keyboard action on this Mac.
const LOCAL_BAR_SECS: u64 = 8;
@@ -30,16 +27,8 @@ pub struct ComputerUi {
pub confirm_reset: bool,
/// 一律允許的應用程式 is expanded.
pub apps_open: bool,
- pub viewer_open: bool,
- /// 取得控制權: the person's clicks and keys go to the box.
+ /// 接管 during a hand-off: this app told the bots' computer to let the person have it.
pub control: bool,
- pub fit: bool,
- texture: Option,
- frame: Arc>>,
- shown_seq: u64,
- frame_size: [usize; 2],
- worker: Option>,
- input_tx: Option>,
pub confirm: Option,
pub note: Arc>,
disk: Arc>>,
@@ -51,8 +40,6 @@ pub struct ComputerUi {
teach_name: String,
teach_for: Option,
teach_error: String,
- /// Screenshot mode: show the real box in the viewer.
- pub demo_live: bool,
schedule_edit: String,
pub update_ready: bool,
update_checked: Option,
@@ -62,16 +49,6 @@ pub struct ComputerUi {
thumb_img: Arc>>,
thumb_busy: Arc,
thumb_at: Option,
- /// The viewer shows the brain's computer (this window is a screen of a brain): how to reach it.
- remote: Option,
-}
-
-enum Input {
- Click(i64, i64, &'static str, u32),
- Move(i64, i64),
- Scroll(i64, i64, bool, u32),
- Key(String),
- Text(String),
}
fn status_color(r: &Run, p: Palette) -> Color32 {
@@ -103,16 +80,6 @@ fn status_pill(ui: &mut egui::Ui, r: &Run, p: Palette) {
}
/// A little monitor drawn with lines (no emoji font needed).
-fn monitor_icon(ui: &mut egui::Ui, size: f32, col: Color32) {
- let (rect, _) = ui.allocate_exact_size(vec2(size, size), egui::Sense::hover());
- let s = size / 20.0;
- let screen = egui::Rect::from_min_size(rect.min + vec2(2.0 * s, 3.0 * s), vec2(16.0 * s, 11.0 * s));
- ui.painter().rect_stroke(screen, 2.5 * s, Stroke::new(1.5, col), egui::StrokeKind::Inside);
- ui.painter().line_segment([rect.min + vec2(10.0 * s, 14.0 * s), rect.min + vec2(10.0 * s, 17.0 * s)], Stroke::new(1.5, col));
- ui.painter().line_segment([rect.min + vec2(6.5 * s, 17.5 * s), rect.min + vec2(13.5 * s, 17.5 * s)], Stroke::new(1.5, col));
-}
-
-
impl App {
/// Called once at start.
pub(super) fn computers_start(&mut self) {
@@ -946,455 +913,67 @@ impl App {
self.cu.thumb.as_ref().map(|t| (t.id(), t.size_vec2()))
}
+ /// 看畫面: the bots' computer in the browser (noVNC), live, where the person can also take
+ /// over. On a screen of a brain it is the brain's computer, through the account server (a
+ /// link made for this once); here it is this computer's own box.
pub(super) fn open_viewer(&mut self) {
- self.cu.viewer_open = true;
- self.cu.fit = true;
+ self.open_screen(false);
+ }
+
+ fn open_screen(&mut self, control: bool) {
self.show_settings = false;
- // A screen of a brain watches the brain's computer (where its bots really work).
+ let name = self.state.bots.iter().find(|b| b.id == self.state.selected).map(|b| format!("{} 的電腦", b.name)).unwrap_or_else(|| "Bot 的電腦".into());
if self.mir.on {
let s = self.state.remote.clone();
- self.cu.remote = Some(s.clone());
- if let Some(stop) = self.cu.worker.take() {
- stop.store(true, Ordering::SeqCst);
- }
- self.cu.texture = None;
- std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "viewer", "on": true}));
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "wake"}));
+ self.box_task(move || {
+ let ticket = crate::remote::screen_ticket(&s)?;
+ let enc = |t: &str| t.bytes().map(|b| if b.is_ascii_alphanumeric() { (b as char).to_string() } else { format!("%{b:02X}") }).collect::();
+ let url = format!("{}/screen#host={}&ticket={ticket}&name={}{}", s.relay_url.trim_end_matches('/'), s.brain_id, enc(&name), if control { "&control=1" } else { "" });
+ crate::preview::open_with_system(&url);
+ Ok(String::new())
});
return;
}
- if self.cu.remote.take().is_some() {
- if let Some(stop) = self.cu.worker.take() {
- stop.store(true, Ordering::SeqCst);
- }
- self.cu.texture = None;
- }
- cloudbox::set_viewer_open(true);
- if !cloudbox::run_state().is_running() && !self.demo {
- self.box_task(|| cloudbox::ensure_running().map(|_| String::new()));
+ if self.demo {
+ return;
}
+ self.box_task(move || {
+ let url = cloudbox::viewer_url(control)?;
+ crate::preview::open_with_system(&url);
+ Ok(String::new())
+ });
}
- /// 接管: open the computer and take control (the bot waits for the person anyway).
+ /// 接管: open the computer with control (the bot waits for the person meanwhile).
pub(super) fn take_over_computer(&mut self) {
- self.open_viewer();
+ self.open_screen(true);
if self.cu.control {
return;
}
self.cu.control = true;
- match self.cu.remote.clone() {
- Some(s) => {
- std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "hold", "on": true}));
- });
- }
- None => cloudbox::HELD_BY_PERSON.store(true, Ordering::SeqCst),
- }
+ self.hold_computer(true);
}
- /// 我完成了 / 跳過: the bot gets its computer back (its steps would otherwise wait for the
- /// person to let go).
+ /// 我完成了 / 跳過: the bot gets its computer back.
pub(super) fn give_back_computer(&mut self) {
if !self.cu.control {
return;
}
self.cu.control = false;
- match self.cu.remote.clone() {
- Some(s) => {
- std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "hold", "on": false}));
- });
- }
- None => cloudbox::HELD_BY_PERSON.store(false, Ordering::SeqCst),
- }
+ self.hold_computer(false);
}
- fn close_viewer(&mut self) {
- self.cu.viewer_open = false;
- self.cu.control = false;
- if let Some(s) = self.cu.remote.clone() {
+ fn hold_computer(&self, on: bool) {
+ if self.mir.on {
+ let s = self.state.remote.clone();
std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "hold", "on": false}));
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "viewer", "on": false}));
+ let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "hold", "on": on}));
});
} else {
- cloudbox::HELD_BY_PERSON.store(false, Ordering::SeqCst);
- cloudbox::set_viewer_open(false);
- }
- if let Some(stop) = self.cu.worker.take() {
- stop.store(true, Ordering::SeqCst);
- }
- self.cu.input_tx = None;
- }
-
- fn ensure_viewer_threads(&mut self, ctx: &egui::Context) {
- if self.cu.worker.is_some() || (self.demo && !self.cu.demo_live) {
- return;
- }
- let stop = Arc::new(AtomicBool::new(false));
- self.cu.worker = Some(stop.clone());
- let slot = self.cu.frame.clone();
- let c = ctx.clone();
- let s2 = stop.clone();
- let brain = self.cu.remote.clone();
- if let Some(s) = brain.clone() {
- // The brain's computer: pictures and the person's input go through the account server.
- let s_frames = s.clone();
- std::thread::spawn(move || {
- let s = s_frames;
- // Ask for the picture after the one shown: the brain answers when the screen
- // changes (no fixed pause between pictures).
- let mut seq = 0u64;
- while !s2.load(Ordering::SeqCst) {
- match crate::remote::sync_live(&s, seq) {
- Ok((n, jpeg)) if n != seq || n == 0 => {
- if let Ok(img) = image::load_from_memory(&jpeg) {
- let img = img.to_rgba8();
- let size = [img.width() as usize, img.height() as usize];
- *slot.lock().unwrap() = Some((n.max(seq + 1), egui::ColorImage::from_rgba_unmultiplied(size, img.as_raw())));
- c.request_repaint();
- }
- // At most about 8 a second over the network (an animated page
- // sends all it can); a brain too old to number them: one a second.
- std::thread::sleep(std::time::Duration::from_millis(if n == 0 { 1000 } else { 120 }));
- seq = n;
- }
- Ok(_) => {}
- Err(_) => {
- std::thread::sleep(std::time::Duration::from_millis(1500));
- c.request_repaint();
- }
- }
- }
- });
- let (tx, rx) = channel::();
- self.cu.input_tx = Some(tx);
- std::thread::spawn(move || {
- let mut last_move = Instant::now();
- while let Ok(i) = rx.recv() {
- let v = match i {
- Input::Click(x, y, b, n) => serde_json::json!({"kind": "click", "x": x, "y": y, "button": b, "count": n}),
- // Hovering sends many moves; a few a second are enough over the network.
- Input::Move(x, y) => {
- if last_move.elapsed().as_millis() < 250 {
- continue;
- }
- last_move = Instant::now();
- serde_json::json!({"kind": "move", "x": x, "y": y})
- }
- Input::Scroll(x, y, down, n) => serde_json::json!({"kind": "scroll", "x": x, "y": y, "down": down, "steps": n}),
- Input::Key(k) => serde_json::json!({"kind": "key", "key": k}),
- Input::Text(t) => serde_json::json!({"kind": "text", "text": t}),
- };
- let _ = crate::remote::sync_post(&s, "screen", &v);
- }
- });
- return;
- }
- std::thread::spawn(move || {
- let mut seq = 0u64;
- while !s2.load(Ordering::SeqCst) {
- match cloudbox::live_frame(seq, std::time::Duration::from_secs(5)) {
- Some((n, jpeg)) if n != seq => {
- if let Ok(img) = image::load_from_memory(&jpeg) {
- let img = img.to_rgba8();
- let size = [img.width() as usize, img.height() as usize];
- *slot.lock().unwrap() = Some((n, egui::ColorImage::from_rgba_unmultiplied(size, img.as_raw())));
- c.request_repaint();
- }
- seq = n;
- }
- Some(_) => {}
- None => {
- std::thread::sleep(std::time::Duration::from_millis(1000));
- c.request_repaint();
- }
- }
- }
- });
- let (tx, rx) = channel::();
- self.cu.input_tx = Some(tx);
- std::thread::spawn(move || {
- while let Ok(i) = rx.recv() {
- match i {
- Input::Click(x, y, b, n) => cloudbox::viewer_click(x, y, b, n),
- Input::Move(x, y) => cloudbox::viewer_move(x, y),
- Input::Scroll(x, y, down, n) => cloudbox::viewer_scroll(x, y, down, n),
- Input::Key(k) => cloudbox::viewer_key(&k),
- Input::Text(t) => cloudbox::viewer_text(&t),
- }
- }
- });
- }
-
- pub(super) fn viewer_window(&mut self, ctx: &egui::Context) {
- if !self.cu.viewer_open {
- return;
- }
- self.ensure_viewer_threads(ctx);
- let p = self.pal;
- if let Some((seq, img)) = self.cu.frame.lock().unwrap().take() {
- if seq != self.cu.shown_seq {
- self.cu.shown_seq = seq;
- self.cu.frame_size = img.size;
- match &mut self.cu.texture {
- Some(t) => t.set(img, egui::TextureOptions::LINEAR),
- None => self.cu.texture = Some(ctx.load_texture("box-screen", img, egui::TextureOptions::LINEAR)),
- }
- }
- }
- let brain = self.cu.remote.is_some();
- // The brain's computer reports its state with the brain's answers.
- let run = if brain {
- match (self.mir.box_state["running"].as_bool().unwrap_or(false), self.mir.box_state["available"].as_bool().unwrap_or(true)) {
- (true, _) => Run::Running,
- (false, false) => Run::NoDocker("大腦那台沒有 Docker".into()),
- _ => Run::Asleep,
- }
- } else {
- cloudbox::run_state()
- };
- let screen = ctx.content_rect();
- let open = true;
- let default = vec2((screen.width() * 0.62).min(900.0), (screen.width() * 0.62).min(900.0) * 0.625 + 52.0);
- let busy = if brain { self.mir.box_state["busy"].as_bool().unwrap_or(false) } else { cloudbox::is_busy() };
- let title = self.state.bot(self.state.selected).map(|b| crate::model::own_computer_name(&b.name)).unwrap_or_else(|| crate::model::BOT_COMPUTER_NAME.to_string());
-
- let mut close_clicked = false;
- let mut done_clicked = false;
- let selected = self.state.selected;
- let handoff = self.approvals.get(&selected).map(|a| a.kind == crate::providers::ApprovalKind::Handoff).unwrap_or(false);
- egui::Window::new("box-viewer")
- .id(egui::Id::unique("box-viewer"))
- .title_bar(false)
- .default_size(default)
- .default_pos(screen.center() - default / 2.0)
- .min_size(vec2(420.0, 300.0))
- .resizable(true)
- .collapsible(false)
- .frame(
- Frame::new()
- .fill(p.elevated)
- .corner_radius(14)
- .inner_margin(Margin::symmetric(14, 12))
- .stroke(Stroke::new(1.0, p.border))
- .shadow(egui::epaint::Shadow { offset: [0, 14], blur: 40, spread: 0, color: Color32::from_black_alpha(60) }),
- )
- .show(ctx, |ui| {
- // Buttons first, from the right; the title and state get what is left and end in
- // … when it's short (the state used to slide under 「取得控制權」 in a narrow window).
- ui.horizontal(|ui| {
- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
- let (rect, r) = ui.allocate_exact_size(vec2(28.0, 28.0), egui::Sense::click());
- if r.hovered() {
- ui.painter().rect_filled(rect, 8, p.hover);
- }
- let c = rect.center();
- let st = Stroke::new(1.5, if r.hovered() { p.text } else { p.text2 });
- ui.painter().line_segment([c + vec2(-5.0, -5.0), c + vec2(5.0, 5.0)], st);
- ui.painter().line_segment([c + vec2(-5.0, 5.0), c + vec2(5.0, -5.0)], st);
- if r.on_hover_text(t("關閉")).clicked() {
- close_clicked = true;
- }
- ui.add_space(4.0);
- // The bot is waiting for the person here: done, and it carries on.
- if handoff && ui.add(egui::Button::new(RichText::new(t("我完成了")).strong().color(p.on_primary)).fill(p.primary).corner_radius(8).min_size(vec2(84.0, 30.0))).clicked() {
- done_clicked = true;
- }
- if !brain && ui.add(secondary_button("全螢幕")).on_hover_text(t("在瀏覽器用 noVNC 全螢幕開啟")).clicked() {
- let control = self.cu.control;
- self.box_task(move || {
- let url = cloudbox::viewer_url(control)?;
- crate::preview::open_with_system(&url);
- Ok(String::new())
- });
- }
- let fit_label = if self.cu.fit { "實際大小" } else { "調整至視窗大小" };
- if ui.add(secondary_button(fit_label)).clicked() {
- self.cu.fit = !self.cu.fit;
- }
- let (label, fill, col) = if self.cu.control { ("交還給 Bot", p.accent, Color32::WHITE) } else { ("取得控制權", p.primary, p.on_primary) };
- let btn = egui::Button::new(RichText::new(label).strong().color(col)).fill(fill).corner_radius(8).min_size(vec2(96.0, 30.0));
- if ui.add_enabled(run.is_running(), btn).on_hover_text(if self.cu.control { "Bot 可以繼續操作" } else { "你自己點擊和輸入;Bot 的操作會先暫停" }).clicked() {
- self.cu.control = !self.cu.control;
- match self.cu.remote.clone() {
- Some(s) => {
- let on = self.cu.control;
- std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "hold", "on": on}));
- });
- }
- None => cloudbox::HELD_BY_PERSON.store(self.cu.control, Ordering::SeqCst),
- }
- }
- ui.add_space(8.0);
- ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
- monitor_icon(ui, 20.0, p.text);
- let note = if self.cu.control { Some(t("你正在操作,Bot 已暫停")) } else if busy { Some(t("Bot 正在操作")) } else { None };
- let room = (ui.available_width() - 96.0 - if note.is_some() { 120.0 } else { 0.0 }).max(60.0);
- ui.label(RichText::new(super::sidebar_ui::shorten_width(ui, &title, room, FONT_LG)).strong().size(FONT_LG));
- if ui.available_width() > 80.0 {
- status_pill(ui, &run, p);
- }
- if let Some(n) = note.filter(|_| ui.available_width() > 110.0) {
- crate::icons::label(ui, if self.cu.control { crate::icons::Icon::Users } else { crate::icons::Icon::Bot }, &n, FONT_XS, p.accent);
- }
- });
- });
- });
- ui.add_space(6.0);
- let avail = ui.available_size();
- match (self.cu.texture.as_ref().map(|t| t.id()), run.is_running()) {
- (Some(tex_id), true) => {
- let [fw, fh] = self.cu.frame_size;
- let (fw, fh) = (fw.max(1) as f32, fh.max(1) as f32);
- let size = if self.cu.fit {
- let k = (avail.x / fw).min((avail.y.max(120.0)) / fh);
- vec2(fw * k, fh * k)
- } else {
- vec2(fw, fh)
- };
- egui::ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
- let sense = if self.cu.control { egui::Sense::click_and_drag() } else { egui::Sense::hover() };
- // Centred in the window (it sat at the left with an empty band beside it).
- let resp = if self.cu.fit {
- let pad = ((ui.available_width() - size.x) / 2.0).max(0.0);
- ui.horizontal(|ui| {
- ui.add_space(pad);
- ui.add(egui::Image::new((tex_id, size)).corner_radius(8).sense(sense))
- })
- .inner
- } else {
- ui.add(egui::Image::new((tex_id, size)).corner_radius(8).sense(sense))
- };
- if self.cu.control {
- ui.painter().rect_stroke(resp.rect, 8, Stroke::new(2.0, p.accent), egui::StrokeKind::Outside);
- self.forward_input(ui, &resp, (fw, fh));
- }
- });
- }
- _ => {
- let (rect, _) = ui.allocate_exact_size(vec2(avail.x, avail.y.max(200.0)), egui::Sense::hover());
- ui.painter().rect_filled(rect, 8, Color32::from_rgb(0x2b, 0x2f, 0x36));
- let text = match &run {
- Run::Running => "正在連線…".to_string(),
- Run::NoDocker(why) => why.clone(),
- Run::Failed(e) => format!("發生問題:{e}"),
- other => format!("{}…", other.label()),
- };
- ui.painter().text(rect.center(), egui::Align2::CENTER_CENTER, text, egui::FontId::proportional(FONT_BASE), Color32::from_gray(220));
- if matches!(run, Run::Asleep | Run::Absent) {
- let b = egui::Rect::from_center_size(rect.center() + vec2(0.0, 34.0), vec2(90.0, 30.0));
- if ui.put(b, primary_button("喚醒", p)).clicked() {
- match self.cu.remote.clone() {
- Some(s) => {
- std::thread::spawn(move || {
- let _ = crate::remote::sync_post(&s, "screen", &serde_json::json!({"kind": "wake"}));
- });
- }
- None => self.box_task(|| cloudbox::ensure_running().map(|_| String::new())),
- }
- }
- }
- }
- }
- });
- if done_clicked {
- self.give_back_computer();
- self.answer(ctx, selected, crate::providers::ApprovalAnswer::Once);
- self.close_viewer();
- } else if close_clicked || !open {
- self.close_viewer();
- }
- ctx.request_repaint_after(std::time::Duration::from_millis(VIEWER_FRAME_MS));
- }
-
- /// Clicks, scrolls and keys on the picture go to the box while the person has control.
- fn forward_input(&mut self, ui: &mut egui::Ui, resp: &egui::Response, frame: (f32, f32)) {
- let Some(tx) = self.cu.input_tx.clone() else { return };
- let to_box = |pos: egui::Pos2| -> (i64, i64) {
- let r = resp.rect;
- (((pos.x - r.left()) / r.width() * frame.0).round() as i64, ((pos.y - r.top()) / r.height() * frame.1).round() as i64)
- };
- if resp.clicked() || resp.secondary_clicked() || resp.double_clicked() {
- resp.request_focus();
- }
- if let Some(pos) = resp.interact_pointer_pos().or(resp.hover_pos()) {
- let (x, y) = to_box(pos);
- if resp.double_clicked() {
- let _ = tx.send(Input::Click(x, y, "left", 2));
- } else if resp.clicked() {
- let _ = tx.send(Input::Click(x, y, "left", 1));
- } else if resp.secondary_clicked() {
- let _ = tx.send(Input::Click(x, y, "right", 1));
- } else if resp.hovered() {
- let d = ui.input(|i| i.smooth_scroll_delta.y);
- if d.abs() > 0.5 {
- let _ = tx.send(Input::Scroll(x, y, d < 0.0, ((d.abs() / 40.0).ceil() as u32).max(1)));
- } else if ui.input(|i| i.pointer.delta().length() > 3.0) {
- let _ = tx.send(Input::Move(x, y));
- }
- }
- }
- if !resp.has_focus() {
- return;
- }
- ui.memory_mut(|m| m.set_focus_lock_filter(resp.id, egui::EventFilter { tab: true, horizontal_arrows: true, vertical_arrows: true, escape: true }));
- let events = ui.input(|i| i.events.clone());
- for e in events {
- match e {
- egui::Event::Text(t) => {
- let _ = tx.send(Input::Text(t));
- }
- egui::Event::Paste(t) => {
- let _ = tx.send(Input::Text(t));
- }
- egui::Event::Key { key, pressed: true, modifiers, .. } => {
- let name = match key {
- Key::Enter => "Return",
- Key::Tab => "Tab",
- Key::Backspace => "BackSpace",
- Key::Delete => "Delete",
- Key::Escape => "Escape",
- Key::ArrowUp => "Up",
- Key::ArrowDown => "Down",
- Key::ArrowLeft => "Left",
- Key::ArrowRight => "Right",
- Key::Home => "Home",
- Key::End => "End",
- Key::PageUp => "Prior",
- Key::PageDown => "Next",
- _ if modifiers.command || modifiers.ctrl || modifiers.alt => {
- let n = key.name();
- if n.chars().count() == 1 {
- n
- } else {
- continue;
- }
- }
- _ => continue,
- };
- let mut combo = vec![];
- if modifiers.command || modifiers.ctrl {
- combo.push("ctrl".to_string());
- }
- if modifiers.alt {
- combo.push("alt".to_string());
- }
- if modifiers.shift && (name.len() > 1 || !combo.is_empty()) {
- combo.push("shift".to_string());
- }
- combo.push(if name.len() == 1 { name.to_lowercase() } else { name.to_string() });
- let _ = tx.send(Input::Key(combo.join("+")));
- }
- _ => {}
- }
+ cloudbox::HELD_BY_PERSON.store(on, Ordering::SeqCst);
}
}
- // ---------- the floating bar while a bot drives this Mac ----------
-
pub(super) fn local_control_bar(&mut self, ctx: &egui::Context) {
if self.demo {
return;
diff --git a/src/cloudbox.rs b/src/cloudbox.rs
index 8d75f28..f19adf6 100644
--- a/src/cloudbox.rs
+++ b/src/cloudbox.rs
@@ -206,6 +206,16 @@ pub fn vnc_password() -> String {
pw
}
+/// The password the box's VNC server really has: the one it was made with (its VNC_PASSWORD), so a
+/// viewer signs in even when this AnyBot's settings were made anew; else this AnyBot's own.
+pub fn box_vnc_password() -> String {
+ dk_timeout(&["exec", container(), "printenv", "VNC_PASSWORD"], 5_000)
+ .ok()
+ .map(|b| String::from_utf8_lossy(&b).trim().to_string())
+ .filter(|p| !p.is_empty())
+ .unwrap_or_else(vnc_password)
+}
+
static BUSY: AtomicUsize = AtomicUsize::new(0);
/// The person took control in the viewer: the bot's screen actions wait.
pub static HELD_BY_PERSON: AtomicBool = AtomicBool::new(false);
@@ -417,6 +427,13 @@ fn docker() -> Command {
c
}
+/// A program in the box with its stdin and stdout piped to this side (as the bot user).
+pub fn exec_piped(args: &[&str]) -> Result {
+ let mut a = vec!["exec", "-i", "-u", "bot", container()];
+ a.extend_from_slice(args);
+ docker().args(&a).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().map_err(|e| format!("找不到 docker:{e}"))
+}
+
/// The `docker …` prefix for shell strings (used by the command tools).
fn docker_prefix() -> String {
let host = docker_host();
@@ -1188,164 +1205,16 @@ pub fn thumbnail(width: u32) -> Option {
image::open(&path).ok().map(|i| i.to_rgba8())
}
-/// The screen now, for a small picture (no waking: None when the box isn't running): the live
-/// picture when someone is watching, else one taken now.
+/// The screen now, for a small picture (no waking: None when the box isn't running). People watch
+/// it live through VNC (vnc.rs).
pub fn frame() -> Option {
if !run_state().is_running() {
return None;
}
- let jpeg = match live_latest() {
- Some(j) => j,
- None => std::sync::Arc::new(dk_timeout(&["exec", container(), "import", "-window", "root", "-display", DISPLAY, "-quality", "80", "jpeg:-"], 5_000).ok()?),
- };
+ let jpeg = dk_timeout(&["exec", container(), "import", "-window", "root", "-display", DISPLAY, "-quality", "80", "jpeg:-"], 5_000).ok()?;
image::load_from_memory(&jpeg).ok().map(|i| i.to_rgba8())
}
-// ---------- the live picture (people watching) ----------
-// One lasting `docker exec` takes the desktop as a JPEG inside the box (ImageMagick, in C) a few
-// times a second and sends a picture only when it changed; a viewer asks for "a picture newer
-// than mine" and gets it the moment there is one. It used to be one exec per picture (a 1 MB
-// PNG, decoded and encoded again as a JPEG here) and a fixed pause after each: about one
-// picture a second, so a click showed its result a second or two later. It stops when nobody
-// has asked for a while.
-
-/// JPEG quality of the live picture (text stays readable; 60–90 KB for 1280×800).
-const LIVE_QUALITY: u8 = 70;
-/// Pause between looks at the screen inside the box: short while it is changing (a click's
-/// result, scrolling), longer while it stays still.
-const LIVE_GAP_BUSY: &str = "0.04";
-const LIVE_GAP_STILL: &str = "0.12";
-/// Stop taking pictures when no viewer asked for this long.
-const LIVE_IDLE: Duration = Duration::from_secs(10);
-
-struct Live {
- seq: u64,
- jpeg: std::sync::Arc>,
- at: Instant,
- asked: Instant,
- running: bool,
-}
-
-fn live() -> &'static (Mutex, std::sync::Condvar) {
- static L: OnceLock<(Mutex, std::sync::Condvar)> = OnceLock::new();
- L.get_or_init(|| (Mutex::new(Live { seq: 0, jpeg: Default::default(), at: Instant::now(), asked: Instant::now(), running: false }), std::sync::Condvar::new()))
-}
-
-/// The latest live picture, when one was taken in the last few seconds.
-fn live_latest() -> Option>> {
- let l = live().0.lock().unwrap_or_else(|e| e.into_inner());
- (l.running && l.seq > 0 && l.at.elapsed() < Duration::from_secs(3)).then(|| l.jpeg.clone())
-}
-
-/// A picture of the desktop newer than `after` (0: any), waiting up to `wait` for the screen to
-/// change; the same one again when it didn't. (number, JPEG); None when the box isn't running.
-pub fn live_frame(after: u64, wait: Duration) -> Option<(u64, std::sync::Arc>)> {
- if !run_state().is_running() {
- return None;
- }
- let (m, cv) = live();
- let mut l = m.lock().unwrap_or_else(|e| e.into_inner());
- l.asked = Instant::now();
- if !l.running {
- l.running = true;
- std::thread::spawn(live_loop);
- }
- let until = Instant::now() + wait;
- while l.seq == 0 || l.seq <= after {
- let left = until.saturating_duration_since(Instant::now());
- if left.is_zero() {
- break;
- }
- l = cv.wait_timeout(l, left).map(|r| r.0).unwrap_or_else(|e| e.into_inner().0);
- }
- (l.seq > 0).then(|| (l.seq, l.jpeg.clone()))
-}
-
-/// The box's side: a picture whenever the screen changed, as "\n", and "0\n" now
-/// and then while it stays still. Ending the docker exec on this side doesn't end the loop in
-/// the box (a still screen never writes, so it never noticed): it ends the one before it when
-/// it starts, and itself when a write fails (the "0" lines make sure one does).
-fn live_script() -> String {
- format!(
- "[ -f /tmp/anybot-live.pid ] && kill $(cat /tmp/anybot-live.pid) 2>/dev/null; echo $$ > /tmp/anybot-live.pid; f=/tmp/anybot-live-$$.jpg; trap 'rm -f $f' EXIT; prev=; still=0; \
- while :; do import -window root -display {DISPLAY} -quality {LIVE_QUALITY} jpeg:$f 2>/dev/null || exit 3; h=$(cksum < $f); \
- if [ \"$h\" != \"$prev\" ]; then prev=$h; still=0; {{ wc -c < $f && cat $f; }} || exit 0; sleep {LIVE_GAP_BUSY}; \
- else still=$((still+1)); if [ $still -ge 15 ]; then still=0; echo 0 || exit 0; fi; sleep {LIVE_GAP_STILL}; fi; done"
- )
-}
-
-/// End the picture loop in the box (see live_script).
-fn live_stop_in_box() {
- let _ = dk_timeout(&["exec", container(), "sh", "-c", "[ -f /tmp/anybot-live.pid ] && kill $(cat /tmp/anybot-live.pid) 2>/dev/null; rm -f /tmp/anybot-live.pid; true"], 5_000);
-}
-
-fn live_loop() {
- use std::io::{BufRead, Read};
- let (m, cv) = live();
- let stop = |m: &Mutex| m.lock().unwrap_or_else(|e| e.into_inner()).running = false;
- let Ok(mut child) = docker().args(["exec", "-u", "bot", container(), "sh", "-c", &live_script()]).stdout(Stdio::piped()).stderr(Stdio::null()).spawn() else {
- return stop(m);
- };
- let Some(out) = child.stdout.take() else {
- let _ = child.kill();
- return stop(m);
- };
- // Reading blocks while the screen stays the same; the watcher ends it when nobody asks.
- let done = std::sync::Arc::new(AtomicBool::new(false));
- let d2 = done.clone();
- let reader = std::thread::spawn(move || {
- let mut r = std::io::BufReader::new(out);
- let mut line = String::new();
- while !d2.load(Ordering::SeqCst) {
- line.clear();
- if r.read_line(&mut line).unwrap_or(0) == 0 {
- break;
- }
- let Ok(n) = line.trim().parse::() else { break };
- // "0": still there, nothing new.
- if n == 0 {
- continue;
- }
- let mut jpeg = vec![0u8; n];
- if r.read_exact(&mut jpeg).is_err() {
- break;
- }
- if !jpeg.starts_with(&[0xff, 0xd8]) {
- continue;
- }
- let mut l = m.lock().unwrap_or_else(|e| e.into_inner());
- l.seq += 1;
- l.jpeg = std::sync::Arc::new(jpeg);
- l.at = Instant::now();
- cv.notify_all();
- }
- d2.store(true, Ordering::SeqCst);
- });
- loop {
- std::thread::sleep(Duration::from_millis(500));
- let idle = m.lock().unwrap_or_else(|e| e.into_inner()).asked.elapsed() > LIVE_IDLE;
- if idle || done.load(Ordering::SeqCst) || !run_state().is_running() {
- break;
- }
- }
- done.store(true, Ordering::SeqCst);
- let _ = child.kill();
- let _ = child.wait();
- if run_state().is_running() {
- live_stop_in_box();
- }
- let _ = reader.join();
- let mut l = m.lock().unwrap_or_else(|e| e.into_inner());
- l.running = false;
- // Viewers waiting on a picture that won't come get the last one now.
- cv.notify_all();
-}
-
-/// The desktop's size (for a viewer mapping its taps), when known.
-pub fn screen_size() -> Option<(u32, u32)> {
- box_size().ok()
-}
-
/// The desktop's size in pixels (asked once per box).
fn box_size() -> Result<(u32, u32), String> {
if let Some(s) = shared().lock().unwrap_or_else(|e| e.into_inner()).size {
@@ -1605,8 +1474,8 @@ pub fn computer_run(name: &str, v: &Value) -> Result<(String, Option), S
}
_ => {}
}
- if HELD_BY_PERSON.load(Ordering::SeqCst) {
- return Err("使用者正在自己操作這台電腦(已取得控制權)。請等他交還控制權後再操作,或先截圖看看畫面。".into());
+ if HELD_BY_PERSON.load(Ordering::SeqCst) || crate::vnc::person_active() {
+ return Err("使用者正在自己操作這台電腦。請等他操作完(停下幾秒後)再繼續,或先截圖看看畫面。".into());
}
let (note, picture) = if name == "computer_use_open_app" {
let note = open_on_box(v["app"].as_str().unwrap_or(""))?;
@@ -1623,85 +1492,13 @@ pub fn computer_run(name: &str, v: &Value) -> Result<(String, Option), S
}
}
-// ---------- the viewer's own input (when the person takes control) ----------
-// Same scripts, at the desktop's own pixels (the viewer shows it full size), no picture back,
-// through one lasting shell in the box (a click or a key no longer starts a docker exec of its
-// own: 50–200 ms each, more with a Docker host elsewhere). Typed text rides along as base64.
-
-fn input_shell() -> &'static Mutex