agent-desktop-0.0.2.js 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. /**
  2. * @description Remote Desktop
  3. * @author Ylian Saint-Hilaire
  4. * @version v0.0.2g
  5. */
  6. // Polyfill Uint8Array.slice() for IE
  7. if (!Uint8Array.prototype.slice) { Object.defineProperty(Uint8Array.prototype, 'slice', { value: function (begin, end) { return new Uint8Array(Array.prototype.slice.call(this, begin, end)); } }); }
  8. function isWindowsBrowser() {
  9. return navigator && !!(/win/i).exec(navigator.platform);
  10. }
  11. // Construct a MeshServer object
  12. var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
  13. var obj = {}
  14. obj.CanvasId = canvasid;
  15. if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
  16. obj.Canvas = obj.CanvasId.getContext('2d');
  17. obj.scrolldiv = scrolldiv;
  18. obj.State = 0;
  19. obj.PendingOperations = [];
  20. obj.tilesReceived = 0;
  21. obj.TilesDrawn = 0;
  22. obj.KillDraw = 0;
  23. obj.ipad = false;
  24. obj.tabletKeyboardVisible = false;
  25. obj.LastX = 0;
  26. obj.LastY = 0;
  27. obj.touchenabled = 0;
  28. obj.submenuoffset = 0;
  29. obj.touchtimer = null;
  30. obj.TouchArray = {};
  31. obj.connectmode = 0; // 0 = HTTP, 1 = WebSocket, 2 = WebRTC
  32. obj.connectioncount = 0;
  33. obj.rotation = 0;
  34. obj.protocol = 2; // KVM
  35. obj.debugmode = 0;
  36. obj.firstUpKeys = [];
  37. obj.stopInput = false;
  38. obj.localKeyMap = true;
  39. obj.remoteKeyMap = false; // If false, the remote keyboard mapping is not used.
  40. obj.pressedKeys = [];
  41. obj._altGrArmed = false; // Windows AltGr detection
  42. obj._altGrTimeout = 0;
  43. obj.isWindowsBrowser = isWindowsBrowser();
  44. obj.sessionid = 0;
  45. obj.username;
  46. obj.oldie = false;
  47. obj.ImageType = 1; // 1 = JPEG, 2 = PNG, 3 = TIFF, 4 = WebP
  48. obj.CompressionLevel = 50;
  49. obj.ScalingLevel = 1024;
  50. obj.FrameRateTimer = 100;
  51. obj.SwapMouse = false;
  52. obj.UseExtendedKeyFlag = true;
  53. obj.FirstDraw = false;
  54. // Remote user mouse and keyboard lock
  55. obj.onRemoteInputLockChanged = null;
  56. obj.RemoteInputLock = null;
  57. // Remote keyboard state
  58. obj.onKeyboardStateChanged = null;
  59. obj.KeyboardState = 0; // 1 = NumLock, 2 = ScrollLock, 4 = CapsLock
  60. obj.ScreenWidth = 960;
  61. obj.ScreenHeight = 701;
  62. obj.width = 960;
  63. obj.height = 960;
  64. obj.displays = null;
  65. obj.selectedDisplay = null;
  66. obj.onScreenSizeChange = null;
  67. obj.onMessage = null;
  68. obj.onConnectCountChanged = null;
  69. obj.onDebugMessage = null;
  70. obj.onTouchEnabledChanged = null;
  71. obj.onDisplayinfo = null;
  72. obj.accumulator = null;
  73. var xMouseCursorActive = true;
  74. var xMouseCursorCurrent = 'default';
  75. obj.mouseCursorActive = function (x) { if (xMouseCursorActive == x) return; xMouseCursorActive = x; obj.CanvasId.style.cursor = ((x == true) ? xMouseCursorCurrent : 'default'); }
  76. var mouseCursors = ['default', 'progress', 'crosshair', 'pointer', 'help', 'text', 'no-drop', 'move', 'nesw-resize', 'ns-resize', 'nwse-resize', 'w-resize', 'alias', 'wait', 'none', 'not-allowed', 'col-resize', 'row-resize', 'copy', 'zoom-in', 'zoom-out'];
  77. obj.Start = function () {
  78. obj.State = 0;
  79. obj.accumulator = null;
  80. }
  81. obj.Stop = function () {
  82. obj.setRotation(0);
  83. obj.UnGrabKeyInput();
  84. obj.UnGrabMouseInput();
  85. obj.touchenabled = 0;
  86. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
  87. obj.Canvas.clearRect(0, 0, obj.CanvasId.width, obj.CanvasId.height);
  88. }
  89. obj.xxStateChange = function (newstate) {
  90. if (obj.State == newstate) return;
  91. obj.State = newstate;
  92. obj.CanvasId.style.cursor = 'default';
  93. //console.log('xxStateChange', newstate);
  94. switch (newstate) {
  95. case 0: {
  96. // Disconnect
  97. obj.Stop();
  98. break;
  99. }
  100. case 3: {
  101. // Websocket connected
  102. break;
  103. }
  104. }
  105. }
  106. obj.send = function (x) {
  107. if (obj.debugmode > 2) { console.log('KSend(' + x.length + '): ' + rstr2hex(x)); }
  108. if (obj.parent != null) { obj.parent.send(x); }
  109. }
  110. // KVM Control.
  111. // Routines for processing incoming packets from the AJAX server, and handling individual messages.
  112. obj.ProcessPictureMsg = function (data, X, Y) {
  113. //if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8));
  114. var tile = new Image();
  115. tile.xcount = obj.tilesReceived++;
  116. var r = obj.tilesReceived, tdata = data.slice(4), ptr = 0, strs = [];
  117. // String.fromCharCode.apply() can't handle very large argument count, so we have to split like this.
  118. while ((tdata.byteLength - ptr) > 50000) { strs.push(String.fromCharCode.apply(null, tdata.slice(ptr, ptr + 50000))); ptr += 50000; }
  119. if (ptr > 0) { strs.push(String.fromCharCode.apply(null, tdata.slice(ptr))); } else { strs.push(String.fromCharCode.apply(null, tdata)); }
  120. tile.src = 'data:image/jpeg;base64,' + btoa(strs.join(''));
  121. tile.onload = function () {
  122. //console.log('DecodeTile #' + this.xcount);
  123. if ((obj.Canvas != null) && (obj.KillDraw < r) && (obj.State != 0)) {
  124. obj.PendingOperations.push([r, 2, tile, X, Y]);
  125. while (obj.DoPendingOperations()) { }
  126. } else {
  127. obj.PendingOperations.push([r, 0]);
  128. }
  129. }
  130. tile.error = function () { console.log('DecodeTileError'); }
  131. }
  132. obj.DoPendingOperations = function () {
  133. if (obj.PendingOperations.length == 0) return false;
  134. for (var i = 0; i < obj.PendingOperations.length; i++) { // && KillDraw < tilesDrawn
  135. var Msg = obj.PendingOperations[i];
  136. if (Msg[0] == (obj.TilesDrawn + 1)) {
  137. if (obj.onPreDrawImage != null) obj.onPreDrawImage(); // Notify that we are about to draw on the canvas.
  138. if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
  139. else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
  140. obj.PendingOperations.splice(i, 1);
  141. obj.TilesDrawn++;
  142. if ((obj.TilesDrawn == obj.tilesReceived) && (obj.KillDraw < obj.TilesDrawn)) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
  143. return true;
  144. }
  145. }
  146. if (obj.oldie && obj.PendingOperations.length > 0) { obj.TilesDrawn++; }
  147. return false;
  148. }
  149. obj.ProcessCopyRectMsg = function (str) {
  150. var SX = ((str.charCodeAt(0) & 0xFF) << 8) + (str.charCodeAt(1) & 0xFF);
  151. var SY = ((str.charCodeAt(2) & 0xFF) << 8) + (str.charCodeAt(3) & 0xFF);
  152. var DX = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
  153. var DY = ((str.charCodeAt(6) & 0xFF) << 8) + (str.charCodeAt(7) & 0xFF);
  154. var WIDTH = ((str.charCodeAt(8) & 0xFF) << 8) + (str.charCodeAt(9) & 0xFF);
  155. var HEIGHT = ((str.charCodeAt(10) & 0xFF) << 8) + (str.charCodeAt(11) & 0xFF);
  156. obj.Canvas.drawImage(Canvas.canvas, SX, SY, WIDTH, HEIGHT, DX, DY, WIDTH, HEIGHT);
  157. }
  158. obj.SendUnPause = function () {
  159. if (obj.debugmode > 1) { console.log('SendUnPause'); }
  160. //obj.xxStateChange(3);
  161. obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
  162. }
  163. obj.SendPause = function () {
  164. if (obj.debugmode > 1) { console.log('SendPause'); }
  165. //obj.xxStateChange(2);
  166. obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
  167. }
  168. obj.SendCompressionLevel = function (type, level, scaling, frametimer) { // Type: 1 = JPEG, 2 = PNG, 3 = TIFF, 4 = WebP
  169. obj.ImageType = type;
  170. if (level) { obj.CompressionLevel = level; }
  171. if (scaling) { obj.ScalingLevel = scaling; }
  172. if (frametimer) { obj.FrameRateTimer = frametimer; }
  173. obj.send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
  174. }
  175. obj.SendRefresh = function () {
  176. obj.send(String.fromCharCode(0x00, 0x06, 0x00, 0x04));
  177. }
  178. obj.ProcessScreenMsg = function (width, height) {
  179. if (obj.debugmode > 0) { console.log('ScreenSize: ' + width + ' x ' + height); }
  180. if ((obj.ScreenWidth == width) && (obj.ScreenHeight == height)) return; // Ignore change if screen is same size.
  181. obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
  182. obj.rotation = 0;
  183. obj.FirstDraw = true;
  184. obj.ScreenWidth = obj.width = width;
  185. obj.ScreenHeight = obj.height = height;
  186. obj.KillDraw = obj.tilesReceived;
  187. while (obj.PendingOperations.length > 0) { obj.PendingOperations.shift(); }
  188. obj.SendCompressionLevel(obj.ImageType);
  189. obj.SendUnPause();
  190. obj.SendRemoteInputLock(2); // Query input lock state
  191. // No need to event the display size change now, it will be evented on first draw.
  192. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
  193. }
  194. obj.ProcessBinaryCommand = function (cmd, cmdsize, view) {
  195. var X, Y;
  196. if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; }
  197. if (obj.debugmode > 2) { console.log('CMD', cmd, cmdsize, X, Y); }
  198. // Fix for view being too large for String.fromCharCode.apply()
  199. var chunkSize = 10000;
  200. let result = '';
  201. for (let i = 0; i < view.length; i += chunkSize) { result += String.fromCharCode.apply(null, view.slice(i, i + chunkSize)); }
  202. // Record the command if needed
  203. if (obj.recordedData != null) {
  204. if (cmdsize > 65000) {
  205. obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdsize) + obj.shortToStr(cmd) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + result));
  206. } else {
  207. obj.recordedData.push(recordingEntry(2, 1, result));
  208. }
  209. }
  210. switch (cmd) {
  211. case 3: // Tile
  212. if (obj.FirstDraw) obj.onResize();
  213. //console.log('TILE', X, Y, cmdsize);
  214. obj.ProcessPictureMsg(view.slice(4), X, Y);
  215. break;
  216. case 7: // Screen size
  217. obj.ProcessScreenMsg(X, Y);
  218. obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
  219. obj.SendKeyMsgKC(obj.KeyAction.UP, 17); // Ctrl
  220. obj.SendKeyMsgKC(obj.KeyAction.UP, 18); // Alt
  221. obj.SendKeyMsgKC(obj.KeyAction.UP, 91); // Left-Windows
  222. obj.SendKeyMsgKC(obj.KeyAction.UP, 92); // Right-Windows
  223. obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
  224. obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04));
  225. break;
  226. case 11: // GetDisplays (TODO)
  227. var selectedDisplay = 0, displays = {}, dcount = (view[4] << 8) + view[5];
  228. if (dcount > 0) {
  229. // Many displays present
  230. selectedDisplay = (view[6 + (dcount * 2)] << 8) + view[7 + (dcount * 2)];
  231. for (var i = 0; i < dcount; i++) {
  232. var disp = (view[6 + (i * 2)] << 8) + view[7 + (i * 2)];
  233. if (disp == 65535) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; }
  234. }
  235. }
  236. //console.log('Get Displays', displays, selectedDisplay, rstr2hex(str));
  237. obj.displays = displays; obj.selectedDisplay = selectedDisplay;
  238. if (obj.onDisplayinfo != null) { obj.onDisplayinfo(obj, displays, selectedDisplay); }
  239. break;
  240. case 12: // SetDisplay
  241. //console.log('SetDisplayConfirmed');
  242. break;
  243. case 14: // KVM_INIT_TOUCH
  244. obj.touchenabled = 1;
  245. obj.TouchArray = {};
  246. if (obj.onTouchEnabledChanged != null) obj.onTouchEnabledChanged(obj.touchenabled);
  247. break;
  248. case 15: // KVM_TOUCH
  249. obj.TouchArray = {};
  250. break;
  251. case 17: // MNG_KVM_MESSAGE
  252. var str = String.fromCharCode.apply(null, view.slice(4));
  253. console.log('Got KVM Message: ' + str);
  254. if (obj.onMessage != null) obj.onMessage(str, obj);
  255. break;
  256. case 18: // MNG_KVM_KEYSTATE
  257. if ((cmdsize != 5) || (obj.KeyboardState == view[4])) break;
  258. obj.KeyboardState = view[4]; // 1 = NumLock, 2 = ScrollLock, 4 = CapsLock
  259. if (obj.onKeyboardStateChanged) { obj.onKeyboardStateChanged(obj, obj.KeyboardState); }
  260. console.log('MNG_KVM_KEYSTATE:' + ((obj.KeyboardState & 1) ? ' NumLock' : '') + ((obj.KeyboardState & 2) ? ' ScrollLock' : '') + ((obj.KeyboardState & 4) ? ' CapsLock' : ''));
  261. break;
  262. case 65: // Alert
  263. var str = String.fromCharCode.apply(null, view.slice(4));
  264. if (str[0] != '.') {
  265. console.log(str); //alert('KVM: ' + str);
  266. if (obj.parent && obj.parent.setConsoleMessage) { obj.parent.setConsoleMessage(str); }
  267. } else {
  268. console.log('KVM: ' + str.substring(1));
  269. }
  270. break;
  271. case 82: // DISPLAY LOCATION & SIZE
  272. if ((cmdsize < 4) || (((cmdsize - 4) % 10) != 0)) break;
  273. var screenCount = ((cmdsize - 4) / 10), screenInfo = {}, ptr = 4;
  274. for (var i = 0; i < screenCount; i++) { screenInfo[(view[ptr + 0] << 8) + view[ptr + 1]] = { x: ((view[ptr + 2] << 8) + view[ptr + 3]), y: ((view[ptr + 4] << 8) + view[ptr + 5]), w: ((view[ptr + 6] << 8) + view[ptr + 7]), h: ((view[ptr + 8] << 8) + view[ptr + 9]) }; ptr += 10; }
  275. //console.log('ScreenInfo', JSON.stringify(screenInfo, null, 2));
  276. break;
  277. case 87: // MNG_KVM_INPUT_LOCK
  278. if (cmdsize != 5) break;
  279. if ((obj.RemoteInputLock == null) || (obj.RemoteInputLock !== (view[4] != 0))) {
  280. obj.RemoteInputLock = (view[4] != 0);
  281. if (obj.onRemoteInputLockChanged) { obj.onRemoteInputLockChanged(obj, obj.RemoteInputLock); }
  282. }
  283. break;
  284. case 88: // MNG_KVM_MOUSE_CURSOR
  285. if ((cmdsize != 5) || (obj.stopInput)) break;
  286. var cursorNum = view[4];
  287. if (cursorNum > mouseCursors.length) { cursorNum = 0; }
  288. xMouseCursorCurrent = mouseCursors[cursorNum];
  289. if (xMouseCursorActive) { obj.CanvasId.style.cursor = xMouseCursorCurrent; }
  290. break;
  291. default:
  292. console.log('Unknown command', cmd, cmdsize);
  293. break;
  294. }
  295. }
  296. // Keyboard and Mouse I/O.
  297. obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 };
  298. obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5, "DBLCLICK": 6 };
  299. obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15, "KEYUNICODE": 85 };
  300. obj.Alternate = 0;
  301. var convertKeyCodeTable = {
  302. "Pause": 19,
  303. "CapsLock": 20,
  304. "Space": 32,
  305. "Quote": 222,
  306. "Minus": 189,
  307. "NumpadMultiply": 106,
  308. "NumpadAdd": 107,
  309. "PrintScreen": 44,
  310. "Comma": 188,
  311. "NumpadSubtract": 109,
  312. "NumpadDecimal": 110,
  313. "Period": 190,
  314. "Slash": 191,
  315. "NumpadDivide": 111,
  316. "Semicolon": 186,
  317. "Equal": 187,
  318. "OSLeft": 91,
  319. "BracketLeft": 219,
  320. "OSRight": 91,
  321. "Backslash": 220,
  322. "BracketRight": 221,
  323. "ContextMenu": 93,
  324. "Backquote": 192,
  325. "NumLock": 144,
  326. "ScrollLock": 145,
  327. "Backspace": 8,
  328. "Tab": 9,
  329. "Enter": 13,
  330. "NumpadEnter": 13,
  331. "Escape": 27,
  332. "Delete": 46,
  333. "Home": 36,
  334. "PageUp": 33,
  335. "PageDown": 34,
  336. "ArrowLeft": 37,
  337. "ArrowUp": 38,
  338. "ArrowRight": 39,
  339. "ArrowDown": 40,
  340. "End": 35,
  341. "Insert": 45,
  342. "F1": 112,
  343. "F2": 113,
  344. "F3": 114,
  345. "F4": 115,
  346. "F5": 116,
  347. "F6": 117,
  348. "F7": 118,
  349. "F8": 119,
  350. "F9": 120,
  351. "F10": 121,
  352. "F11": 122,
  353. "F12": 123,
  354. "ShiftLeft": 16,
  355. "ShiftRight": 16,
  356. "ControlLeft": 17,
  357. "ControlRight": 17,
  358. "AltLeft": 18,
  359. "AltRight": 18,
  360. "MetaLeft": 91,
  361. "MetaRight": 92,
  362. "VolumeMute": 181
  363. //"LaunchMail":
  364. //"LaunchApp1":
  365. //"LaunchApp2":
  366. //"BrowserStop":
  367. //"MediaStop":
  368. //"MediaTrackPrevious":
  369. //"MediaTrackNext":
  370. //"MediaPlayPause":
  371. //"MediaSelect":
  372. }
  373. function convertKeyCode(e) {
  374. if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3); }
  375. if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
  376. if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6) + 48; }
  377. return convertKeyCodeTable[e.code];
  378. }
  379. var extendedKeyTable = ['ShiftRight', 'AltRight', 'ControlRight', 'Home', 'End', 'Insert', 'Delete', 'PageUp', 'PageDown', 'NumpadDivide', 'NumpadEnter', 'NumLock', 'Pause'];
  380. obj.SendKeyMsg = function (action, event) {
  381. if (action == null) return;
  382. if (!event) { event = window.event; }
  383. var extendedKey = false; // Test feature, add ?extkeys=1 to url to use.
  384. if ((obj.UseExtendedKeyFlag || (urlargs.extkeys == 1)) && (typeof event.code == 'string') && (event.code.startsWith('Arrow') || (extendedKeyTable.indexOf(event.code) >= 0))) {
  385. extendedKey = true;
  386. }
  387. if (obj.isWindowsBrowser) {
  388. if( obj.checkAltGr(obj, event, action) ) {
  389. return;
  390. };
  391. }
  392. if ((extendedKey == false) && event.code && (event.code.startsWith('NumPad') == false) && (obj.localKeyMap == false)) {
  393. // Convert "event.code" into a scancode. This works the same regardless of the keyboard language.
  394. // Older browsers will not support this.
  395. var kc = convertKeyCode(event);
  396. if (kc != null) { obj.SendKeyMsgKC(action, kc, extendedKey); }
  397. } else {
  398. // Use this keycode, this works best with "US-EN" keyboards.
  399. // Older browser support this.
  400. var kc = event.keyCode;
  401. if (kc == 0x3B) { kc = 0xBA; } // Fix the ';' key
  402. else if (kc == 173) { kc = 189; } // Fix the '-' key for Firefox
  403. else if (kc == 61) { kc = 187; } // Fix the '=' key for Firefox
  404. obj.SendKeyMsgKC(action, kc, extendedKey);
  405. }
  406. }
  407. const ControlLeftKc = 17;
  408. const AltGrKc = 225;
  409. //return true: Key is alredy handled.
  410. obj.checkAltGr = function (obj, event, action) {
  411. // Windows doesn't have a proper AltGr, but handles it using
  412. // fake Ctrl+Alt. However the remote end might not be Windows,
  413. // so we need to merge those into a single AltGr event. We
  414. // detect this case by seeing the two key events directly after
  415. // each other with a very short time between them (<50ms).
  416. if (obj._altGrArmed) {
  417. obj._altGrArmed = false;
  418. clearTimeout(obj._altGrTimeout);
  419. if ((event.code === "AltRight") && ((event.timeStamp - obj._altGrCtrlTime) < 50)) {
  420. //AltGr detected.
  421. obj.SendKeyMsgKC( action, AltGrKc, false);
  422. return true;
  423. }
  424. }
  425. // Possible start of AltGr sequence?
  426. if ((event.code === "ControlLeft") && !(ControlLeftKc in obj.pressedKeys)) {
  427. obj._altGrArmed = true;
  428. obj._altGrCtrlTime = event.timeStamp;
  429. if( action == 1 ) {
  430. obj._altGrTimeout = setTimeout(obj._handleAltGrTimeout.bind(obj), 100);
  431. return true;
  432. }
  433. }
  434. return false;
  435. }
  436. obj._handleAltGrTimeout = function () { //Windows and no Ctrl+Alt -> send only Ctrl.
  437. obj._altGrArmed = false;
  438. clearTimeout(obj._altGrTimeout);
  439. obj.SendKeyMsgKC( 1, ControlLeftKc, false); // (KeyDown, "ControlLeft", false)
  440. }
  441. // Send remote input lock. 0 = Unlock, 1 = Lock, 2 = Query
  442. obj.SendRemoteInputLock = function (code) { obj.send(String.fromCharCode(0x00, 87, 0x00, 0x05, code)); }
  443. obj.SendMessage = function (msg) {
  444. if (obj.State == 3) obj.send(String.fromCharCode(0x00, 0x11) + obj.shortToStr(4 + msg.length) + msg); // 0x11 = 17 MNG_KVM_MESSAGE
  445. }
  446. obj.SendKeyMsgKC = function (action, kc, extendedKey) {
  447. if (obj.State != 3) return;
  448. if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1], action[i][2]); } }
  449. else {
  450. if (action == 1) { // Key Down
  451. if (obj.pressedKeys.indexOf(kc) == -1) { obj.pressedKeys.unshift(kc); } // Add key press to start of array
  452. } else if (action == 2) { // Key Up
  453. var i = obj.pressedKeys.indexOf(kc);
  454. if (i != -1) { obj.pressedKeys.splice(i, 1); } // Remove the key press from the pressed array
  455. }
  456. if (obj.debugmode > 0) { console.log('Sending Key ' + kc + ', action ' + action); }
  457. var up = (action - 1);
  458. if (extendedKey) { if (up == 1) { up = 3; } else { up = 4; } }
  459. obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, up, kc));
  460. }
  461. }
  462. obj.SendStringUnicode = function (str) {
  463. if (obj.State != 3) return;
  464. for (var i = 0; i < str.length; i++) {
  465. obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, 0) + ShortToStr(str.charCodeAt(i)));
  466. obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, 1) + ShortToStr(str.charCodeAt(i)));
  467. }
  468. }
  469. obj.SendKeyUnicode = function (action, val) {
  470. if (obj.State != 3) return;
  471. if (obj.debugmode > 0) { console.log('Sending UnicodeKey ' + val + ', action ' + action); }
  472. obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, (action - 1)) + ShortToStr(val));
  473. }
  474. obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
  475. obj.SendCtrlAltDelMsg = function () {
  476. if (obj.State == 3) { obj.send(String.fromCharCode(0x00, obj.InputType.CTRLALTDEL, 0x00, 0x04)); }
  477. }
  478. obj.SendEscKey = function () {
  479. if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, 0x00, 0x1B, 0x00, obj.InputType.KEY, 0x00, 0x06, 0x01, 0x1B));
  480. }
  481. obj.SendStartMsg = function () {
  482. obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
  483. obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
  484. }
  485. obj.SendCharmsMsg = function () {
  486. obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
  487. obj.SendKeyMsgKC(obj.KeyAction.DOWN, 67); // C
  488. obj.SendKeyMsgKC(obj.KeyAction.UP, 67); // C
  489. obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
  490. }
  491. obj.SendTouchMsg1 = function (id, flags, x, y) {
  492. if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(14) + String.fromCharCode(0x01, id) + obj.intToStr(flags) + obj.shortToStr(x) + obj.shortToStr(y));
  493. }
  494. obj.SendTouchMsg2 = function (id, flags) {
  495. var msg = '';
  496. var flags2;
  497. var str = "TOUCHSEND: ";
  498. for (var k in obj.TouchArray) {
  499. if (k == id) { flags2 = flags; } else {
  500. if (obj.TouchArray[k].f == 1) { flags2 = 0x00010000 | 0x00000002 | 0x00000004; obj.TouchArray[k].f = 3; str += "START" + k; } // POINTER_FLAG_DOWN
  501. else if (obj.TouchArray[k].f == 2) { flags2 = 0x00040000; str += "STOP" + k; } // POINTER_FLAG_UP
  502. else flags2 = 0x00000002 | 0x00000004 | 0x00020000; // POINTER_FLAG_UPDATE
  503. }
  504. msg += String.fromCharCode(k) + obj.intToStr(flags2) + obj.shortToStr(obj.TouchArray[k].x) + obj.shortToStr(obj.TouchArray[k].y);
  505. if (obj.TouchArray[k].f == 2) delete obj.TouchArray[k];
  506. }
  507. if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(5 + msg.length) + String.fromCharCode(0x02) + msg);
  508. if (Object.keys(obj.TouchArray).length == 0 && obj.touchtimer != null) { clearInterval(obj.touchtimer); obj.touchtimer = null; }
  509. }
  510. obj.SendMouseMsg = function (Action, event) {
  511. if (obj.State != 3) return;
  512. if (Action != null && obj.Canvas != null) {
  513. if (!event) { var event = window.event; }
  514. var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
  515. var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
  516. var Offsets = obj.GetPositionOfControl(obj.Canvas.canvas);
  517. var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
  518. var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
  519. if (event.addx) { X += event.addx; }
  520. if (event.addy) { Y += event.addy; }
  521. if (X >= 0 && X <= obj.Canvas.canvas.width && Y >= 0 && Y <= obj.Canvas.canvas.height) {
  522. var Button = 0;
  523. var Delta = 0;
  524. if (Action == obj.KeyAction.UP || Action == obj.KeyAction.DOWN) {
  525. if (event.which) { ((event.which == 1) ? (Button = obj.MouseButton.LEFT) : ((event.which == 2) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
  526. else if (typeof event.button == 'number') { ((event.button == 0) ? (Button = obj.MouseButton.LEFT) : ((event.button == 1) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
  527. }
  528. else if (Action == obj.KeyAction.SCROLL) {
  529. if (event.detail) { Delta = (-1 * (event.detail * 120)); } else if (event.wheelDelta) { Delta = (event.wheelDelta * 3); }
  530. }
  531. // Swap mouse buttons if needed
  532. if (obj.SwapMouse === true) {
  533. if (Button == obj.MouseButton.LEFT) { Button = obj.MouseButton.RIGHT; }
  534. else if (Button == obj.MouseButton.RIGHT) { Button = obj.MouseButton.LEFT; }
  535. }
  536. // Reverse mouse wheel if needed
  537. if (obj.ReverseMouseWheel) { Delta = -1 * Delta; }
  538. var MouseMsg = "";
  539. if (Action == obj.KeyAction.DBLCLICK) {
  540. MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, 0x88, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
  541. } else if (Action == obj.KeyAction.SCROLL) {
  542. var deltaHigh = 0, deltaLow = 0;
  543. if (Delta < 0) { deltaHigh = (255 - (Math.abs(Delta) >> 8)); deltaLow = (255 - (Math.abs(Delta) & 0xFF)); } else { deltaHigh = (Delta >> 8); deltaLow = (Delta & 0xFF); }
  544. MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, 0x00, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF), deltaHigh, deltaLow);
  545. } else {
  546. MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
  547. }
  548. if (obj.Action == obj.KeyAction.NONE) {
  549. if (obj.Alternate == 0 || obj.ipad) { obj.send(MouseMsg); obj.Alternate = 1; } else { obj.Alternate = 0; }
  550. } else {
  551. obj.send(MouseMsg);
  552. }
  553. }
  554. }
  555. }
  556. obj.GetDisplayNumbers = function () { obj.send(String.fromCharCode(0x00, 0x0B, 0x00, 0x04)); } // Get Terminal display
  557. obj.SetDisplay = function (number) { /*console.log('Set display', number);*/ obj.send(String.fromCharCode(0x00, 0x0C, 0x00, 0x06, number >> 8, number & 0xFF)); } // Set Terminal display
  558. obj.intToStr = function (x) { return String.fromCharCode((x >> 24) & 0xFF, (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF); }
  559. obj.shortToStr = function (x) { return String.fromCharCode((x >> 8) & 0xFF, x & 0xFF); }
  560. obj.onResize = function () {
  561. if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
  562. if ((obj.Canvas.canvas.width == obj.ScreenWidth) && (obj.Canvas.canvas.height == obj.ScreenHeight)) return;
  563. if (obj.FirstDraw) {
  564. obj.Canvas.canvas.width = obj.ScreenWidth;
  565. obj.Canvas.canvas.height = obj.ScreenHeight;
  566. obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
  567. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
  568. }
  569. obj.FirstDraw = false;
  570. if (obj.debugmode > 1) { console.log("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight); }
  571. }
  572. obj.xxMouseInputGrab = false;
  573. obj.xxKeyInputGrab = false;
  574. obj.xxMouseMove = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.NONE, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  575. obj.xxMouseUp = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.UP, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  576. obj.xxMouseDown = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DOWN, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  577. obj.xxMouseDblClick = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DBLCLICK, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  578. obj.xxDOMMouseScroll = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
  579. obj.xxMouseWheel = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
  580. obj.xxKeyUp = function (e) {
  581. if ((e.key != 'Dead') && (obj.State == 3)) {
  582. if ((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false)) {
  583. obj.SendKeyUnicode(obj.KeyAction.UP, e.key.charCodeAt(0));
  584. } else {
  585. obj.SendKeyMsg(obj.KeyAction.UP, e);
  586. }
  587. }
  588. if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
  589. }
  590. obj.xxKeyDown = function (e) {
  591. if ((e.key != 'Dead') && (obj.State == 3)) {
  592. if (!((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false))) {
  593. obj.SendKeyMsg(obj.KeyAction.DOWN, e);
  594. if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
  595. }
  596. }
  597. }
  598. obj.xxKeyPress = function (e) {
  599. if ((e.key != 'Dead') && (obj.State == 3)) {
  600. if ((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false)) {
  601. obj.SendKeyUnicode(obj.KeyAction.DOWN, e.key.charCodeAt(0));
  602. } // else { obj.SendKeyMsg(obj.KeyAction.DOWN, e); }
  603. }
  604. if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
  605. }
  606. // Key handlers
  607. obj.handleKeys = function (e) {
  608. //console.log('keypress', e.code, e.key, e.keyCode, (e.key.length == 1) ? e.key.charCodeAt(0) : 0);
  609. if (obj.stopInput == true || desktop.State != 3) return false;
  610. return obj.xxKeyPress(e);
  611. }
  612. obj.handleKeyUp = function (e) {
  613. //console.log('keyup', e.code, e.key, e.keyCode, (e.key.length == 1)?e.key.charCodeAt(0):0);
  614. if (obj.stopInput == true || desktop.State != 3) return false;
  615. if (obj.firstUpKeys.length < 5) {
  616. obj.firstUpKeys.push(e.keyCode);
  617. if ((obj.firstUpKeys.length == 5)) { var j = obj.firstUpKeys.join(','); if ((j == '16,17,91,91,16') || (j == '16,17,18,91,92')) { obj.stopInput = true; } }
  618. }
  619. return obj.xxKeyUp(e);
  620. }
  621. obj.handleKeyDown = function (e) {
  622. //console.log('keydown', e.code, e.key, e.keyCode, (e.key.length == 1) ? e.key.charCodeAt(0) : 0);
  623. if (obj.stopInput == true || desktop.State != 3) return false;
  624. return obj.xxKeyDown(e);
  625. }
  626. // Release the CTRL, ALT, SHIFT keys if they are pressed.
  627. obj.handleReleaseKeys = function () {
  628. var p = JSON.parse(JSON.stringify(obj.pressedKeys)); // Clone the pressed array
  629. for (var i in p) { obj.SendKeyMsgKC(obj.KeyAction.UP, p[i]); } // Release all keys
  630. }
  631. // Mouse handlers
  632. obj.mousedblclick = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDblClick(e); }
  633. obj.mousedown = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDown(e); }
  634. obj.mouseup = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseUp(e); }
  635. obj.mousemove = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseMove(e); }
  636. obj.mousewheel = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseWheel(e); }
  637. obj.xxMsTouchEvent = function (evt) {
  638. if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.
  639. if (evt.preventDefault) evt.preventDefault();
  640. if (evt.stopPropagation) evt.stopPropagation();
  641. if (evt.type == 'MSPointerDown' || evt.type == 'MSPointerMove' || evt.type == 'MSPointerUp') {
  642. var flags = 0;
  643. var id = evt.originalEvent.pointerId % 256;
  644. var X = evt.offsetX * (Canvas.canvas.width / obj.CanvasId.clientWidth);
  645. var Y = evt.offsetY * (Canvas.canvas.height / obj.CanvasId.clientHeight);
  646. if (evt.type == 'MSPointerDown') flags = 0x00010000 | 0x00000002 | 0x00000004; // POINTER_FLAG_DOWN
  647. else if (evt.type == 'MSPointerMove') {
  648. //if (obj.TouchArray[id] && MuchTheSame(obj.TouchArray[id].x, X) && MuchTheSame(obj.TouchArray[id].y, Y)) return;
  649. flags = 0x00020000 | 0x00000002 | 0x00000004; // POINTER_FLAG_UPDATE
  650. }
  651. else if (evt.type == 'MSPointerUp') flags = 0x00040000; // POINTER_FLAG_UP
  652. if (!obj.TouchArray[id]) obj.TouchArray[id] = { x: X, y : Y };
  653. obj.SendTouchMsg2(id, flags)
  654. if (evt.type == 'MSPointerUp') delete obj.TouchArray[id];
  655. } else {
  656. alert(evt.type);
  657. }
  658. return true;
  659. }
  660. obj.xxTouchStart = function (e) {
  661. if (obj.State != 3) return;
  662. if (e.preventDefault) e.preventDefault();
  663. if (obj.touchenabled == 0 || obj.touchenabled == 1) {
  664. if (e.originalEvent.touches.length > 1) return;
  665. var t = e.originalEvent.touches[0];
  666. e.which = 1;
  667. obj.LastX = e.pageX = t.pageX;
  668. obj.LastY = e.pageY = t.pageY;
  669. obj.SendMouseMsg(KeyAction.DOWN, e);
  670. } else {
  671. var Offsets = obj.GetPositionOfControl(Canvas.canvas);
  672. for (var i in e.originalEvent.changedTouches) {
  673. if (!e.originalEvent.changedTouches[i].identifier) continue;
  674. var id = e.originalEvent.changedTouches[i].identifier % 256;
  675. if (!obj.TouchArray[id]) { obj.TouchArray[id] = { x: (e.originalEvent.touches[i].pageX - Offsets[0]) * (Canvas.canvas.width / obj.CanvasId.clientWidth), y: (e.originalEvent.touches[i].pageY - Offsets[1]) * (Canvas.canvas.height / obj.CanvasId.clientHeight), f: 1 }; }
  676. }
  677. if (Object.keys(obj.TouchArray).length > 0 && touchtimer == null) { obj.touchtimer = setInterval(function () { obj.SendTouchMsg2(256, 0); }, 50); }
  678. }
  679. }
  680. obj.xxTouchMove = function (e) {
  681. if (obj.State != 3) return;
  682. if (e.preventDefault) e.preventDefault();
  683. if (obj.touchenabled == 0 || obj.touchenabled == 1) {
  684. if (e.originalEvent.touches.length > 1) return;
  685. var t = e.originalEvent.touches[0];
  686. e.which = 1;
  687. obj.LastX = e.pageX = t.pageX;
  688. obj.LastY = e.pageY = t.pageY;
  689. obj.SendMouseMsg(obj.KeyAction.NONE, e);
  690. } else {
  691. var Offsets = obj.GetPositionOfControl(Canvas.canvas);
  692. for (var i in e.originalEvent.changedTouches) {
  693. if (!e.originalEvent.changedTouches[i].identifier) continue;
  694. var id = e.originalEvent.changedTouches[i].identifier % 256;
  695. if (obj.TouchArray[id]) {
  696. obj.TouchArray[id].x = (e.originalEvent.touches[i].pageX - Offsets[0]) * (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
  697. obj.TouchArray[id].y = (e.originalEvent.touches[i].pageY - Offsets[1]) * (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
  698. }
  699. }
  700. }
  701. }
  702. obj.xxTouchEnd = function (e) {
  703. if (obj.State != 3) return;
  704. if (e.preventDefault) e.preventDefault();
  705. if (obj.touchenabled == 0 || obj.touchenabled == 1) {
  706. if (e.originalEvent.touches.length > 1) return;
  707. e.which = 1;
  708. e.pageX = LastX;
  709. e.pageY = LastY;
  710. obj.SendMouseMsg(KeyAction.UP, e);
  711. } else {
  712. for (var i in e.originalEvent.changedTouches) {
  713. if (!e.originalEvent.changedTouches[i].identifier) continue;
  714. var id = e.originalEvent.changedTouches[i].identifier % 256;
  715. if (obj.TouchArray[id]) obj.TouchArray[id].f = 2;
  716. }
  717. }
  718. }
  719. obj.GrabMouseInput = function () {
  720. if (obj.xxMouseInputGrab == true) return;
  721. var c = obj.CanvasId;
  722. c.onmousemove = obj.xxMouseMove;
  723. c.onmouseup = obj.xxMouseUp;
  724. c.onmousedown = obj.xxMouseDown;
  725. c.touchstart = obj.xxTouchStart;
  726. c.touchmove = obj.xxTouchMove;
  727. c.touchend = obj.xxTouchEnd;
  728. c.MSPointerDown = obj.xxMsTouchEvent;
  729. c.MSPointerMove = obj.xxMsTouchEvent;
  730. c.MSPointerUp = obj.xxMsTouchEvent;
  731. if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
  732. obj.xxMouseInputGrab = true;
  733. }
  734. obj.UnGrabMouseInput = function () {
  735. if (obj.xxMouseInputGrab == false) return;
  736. var c = obj.CanvasId;
  737. c.onmousemove = null;
  738. c.onmouseup = null;
  739. c.onmousedown = null;
  740. c.touchstart = null;
  741. c.touchmove = null;
  742. c.touchend = null;
  743. c.MSPointerDown = null;
  744. c.MSPointerMove = null;
  745. c.MSPointerUp = null;
  746. if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
  747. obj.xxMouseInputGrab = false;
  748. }
  749. obj.GrabKeyInput = function () {
  750. if (obj.xxKeyInputGrab == true) return;
  751. document.onkeyup = obj.xxKeyUp;
  752. document.onkeydown = obj.xxKeyDown;
  753. document.onkeypress = obj.xxKeyPress;
  754. obj.xxKeyInputGrab = true;
  755. }
  756. obj.UnGrabKeyInput = function () {
  757. if (obj.xxKeyInputGrab == false) return;
  758. document.onkeyup = null;
  759. document.onkeydown = null;
  760. document.onkeypress = null;
  761. obj.xxKeyInputGrab = false;
  762. }
  763. obj.GetPositionOfControl = function (Control) {
  764. var Position = Array(2);
  765. Position[0] = Position[1] = 0;
  766. while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
  767. return Position;
  768. }
  769. obj.crotX = function (x, y) {
  770. if (obj.rotation == 0) return x;
  771. if (obj.rotation == 1) return y;
  772. if (obj.rotation == 2) return obj.Canvas.canvas.width - x;
  773. if (obj.rotation == 3) return obj.Canvas.canvas.height - y;
  774. }
  775. obj.crotY = function (x, y) {
  776. if (obj.rotation == 0) return y;
  777. if (obj.rotation == 1) return obj.Canvas.canvas.width - x;
  778. if (obj.rotation == 2) return obj.Canvas.canvas.height - y;
  779. if (obj.rotation == 3) return x;
  780. }
  781. obj.rotX = function (x, y) {
  782. if (obj.rotation == 0 || obj.rotation == 1) return x;
  783. if (obj.rotation == 2) return x - obj.Canvas.canvas.width;
  784. if (obj.rotation == 3) return x - obj.Canvas.canvas.height;
  785. }
  786. obj.rotY = function (x, y) {
  787. if (obj.rotation == 0 || obj.rotation == 3) return y;
  788. if (obj.rotation == 1) return y - obj.Canvas.canvas.width;
  789. if (obj.rotation == 2) return y - obj.Canvas.canvas.height;
  790. }
  791. obj.tcanvas = null;
  792. obj.setRotation = function (x) {
  793. while (x < 0) { x += 4; }
  794. var newrotation = x % 4;
  795. if (newrotation == obj.rotation) return true;
  796. var rw = obj.Canvas.canvas.width;
  797. var rh = obj.Canvas.canvas.height;
  798. if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.Canvas.canvas.height; rh = obj.Canvas.canvas.width; }
  799. // Copy the canvas, put it back in the correct direction
  800. if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
  801. var tcanvasctx = obj.tcanvas.getContext('2d');
  802. tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
  803. tcanvasctx.canvas.width = rw;
  804. tcanvasctx.canvas.height = rh;
  805. tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
  806. if (obj.rotation == 0) tcanvasctx.drawImage(obj.Canvas.canvas, 0, 0);
  807. if (obj.rotation == 1) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, 0);
  808. if (obj.rotation == 2) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, -obj.Canvas.canvas.height);
  809. if (obj.rotation == 3) tcanvasctx.drawImage(obj.Canvas.canvas, 0, -obj.Canvas.canvas.height);
  810. // Change the size and orientation and copy the canvas back into the rotation
  811. if (obj.rotation == 0 || obj.rotation == 2) { obj.Canvas.canvas.height = rw; obj.Canvas.canvas.width = rh; }
  812. if (obj.rotation == 1 || obj.rotation == 3) { obj.Canvas.canvas.height = rh; obj.Canvas.canvas.width = rw; }
  813. obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
  814. obj.Canvas.rotate((newrotation * 90) * Math.PI / 180);
  815. obj.rotation = newrotation;
  816. obj.Canvas.drawImage(obj.tcanvas, obj.rotX(0, 0), obj.rotY(0, 0));
  817. obj.ScreenWidth = obj.Canvas.canvas.width;
  818. obj.ScreenHeight = obj.Canvas.canvas.height;
  819. if (obj.onScreenSizeChange != null) { console.log('s4', obj.ScreenWidth, obj.ScreenHeight); obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
  820. return true;
  821. }
  822. obj.StartRecording = function () {
  823. if (obj.recordedData != null) return;
  824. // Take a screen shot and save it to file
  825. obj.CanvasId['toBlob'](function (blob) {
  826. var fileReader = new FileReader();
  827. fileReader.readAsArrayBuffer(blob);
  828. fileReader.onload = function (event) {
  829. // This is an ArrayBuffer, convert it to a string array
  830. var binary = '', bytes = new Uint8Array(fileReader.result), length = bytes.byteLength;
  831. for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
  832. obj.recordedData = [];
  833. obj.recordedStart = Date.now();
  834. obj.recordedSize = 0;
  835. obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 2 }))); // Metadata (nodeid: obj.nodeid)
  836. obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(7) + obj.shortToStr(8) + obj.shortToStr(obj.ScreenWidth) + obj.shortToStr(obj.ScreenHeight))); // Screen width and height
  837. // Save a screenshot
  838. var cmdlen = (8 + binary.length);
  839. if (cmdlen > 65000) {
  840. // Jumbo Packet
  841. obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdlen) + obj.shortToStr(3) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + binary));
  842. } else {
  843. // Normal packet
  844. obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(3) + obj.shortToStr(cmdlen) + obj.shortToStr(0) + obj.shortToStr(0) + binary));
  845. }
  846. };
  847. });
  848. }
  849. obj.StopRecording = function () {
  850. if (obj.recordedData == null) return;
  851. var r = obj.recordedData;
  852. r.push(recordingEntry(3, 0, 'MeshCentralMCREC'));
  853. delete obj.recordedData;
  854. delete obj.recordedStart;
  855. delete obj.recordedSize;
  856. return r;
  857. }
  858. function recordingEntry(type, flags, data) {
  859. // Header: Type (2) + Flags (2) + Size(4) + Time(8)
  860. // Type (1 = Header, 2 = Network Data), Flags (1 = Binary, 2 = User), Size (4 bytes), Time (8 bytes)
  861. var now = Date.now();
  862. if (typeof data == 'number') {
  863. obj.recordedSize += data;
  864. return obj.shortToStr(type) + obj.shortToStr(flags) + obj.intToStr(data) + obj.intToStr(now >> 32) + obj.intToStr(now & 32);
  865. } else {
  866. obj.recordedSize += data.length;
  867. return obj.shortToStr(type) + obj.shortToStr(flags) + obj.intToStr(data.length) + obj.intToStr(now >> 32) + obj.intToStr(now & 32) + data;
  868. }
  869. }
  870. // Private method
  871. obj.MuchTheSame = function (a, b) { return (Math.abs(a - b) < 4); }
  872. obj.Debug = function (msg) { console.log(msg); }
  873. obj.getIEVersion = function () { var r = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})"); if (re.exec(ua) != null) r = parseFloat(RegExp.$1); } return r; }
  874. obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  875. return obj;
  876. }