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