Improved multi-desktop support

Ylian Saint-Hilaire committed Mar 30, 2018 at 15:26 UTC 2526a0841c1769d5e9dff48863d04e191ed71f83
12 files changed +249 -58
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
mpsserver.js
+1 -1
@@ -226,7 +226,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
226 var meshIdStart = '/' + username;
227 obj.db.GetAllType('mesh', function (err, docs) {
228 var mesh = null;
229 - for (var i in docs) { if (docs[i]._id.indexOf(meshIdStart) > 0) { mesh = docs[i]; break; } }
229 + for (var i in docs) { if (docs[i]._id.replace(/\@/g, 'X').replace(/\$/g, 'X').indexOf(meshIdStart) > 0) { mesh = docs[i]; break; } }
230 if (mesh == null) { Debug(1, 'MPS:Mesh not found', username, password); SendUserAuthFail(socket); return -1; }
231
232 // If this is a agent-less mesh, use the device guid 3 times as ID.
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.5-k",
3 + "version": "0.1.5-r",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/agent-desktop-0.0.2.js
+1 -1
@@ -159,7 +159,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
159 }
160
161 obj.ProcessScreenMsg = function (width, height) {
162 - obj.Debug("ScreenSize: " + width + " x " + height);
162 + //obj.Debug("ScreenSize: " + width + " x " + height);
163 obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
164 obj.rotation = 0;
165 obj.FirstDraw = true;
public/scripts/agent-redir-ws-0.1.0.js
+19 -17
@@ -70,7 +70,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
70 }
71 }
72
73 - function sendCtrlMsg(x) { if (obj.ctrlMsgAllowed == true) { try { obj.socket.send(x); } catch (e) { } } }
73 + function sendCtrlMsg(x) { if (obj.ctrlMsgAllowed == true) { try { obj.socket.send(x); } catch (ex) { } } }
74
75 function performWebRtcSwitch() {
76 if ((obj.webSwitchOk == true) && (obj.webRtcActive == true)) {
@@ -94,7 +94,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
94 //console.log('Recv', e.data, obj.State);
95 if (obj.State < 3) {
96 if (e.data == 'c') {
97 - obj.socket.send(obj.protocol);
97 + try { obj.socket.send(obj.protocol); } catch (ex) { }
98 obj.xxStateChange(3);
99
100 if (obj.attemptWebRTC == true) {
@@ -109,7 +109,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
109 obj.webchannel.onclose = function (event) { /*console.log('WebRTC close');*/ if (obj.webRtcActive) { obj.Stop(); } }
110 obj.webrtc.onicecandidate = function (e) {
111 if (e.candidate == null) {
112 - obj.socket.send(JSON.stringify(obj.webrtcoffer)); // End of candidates, send the offer
112 + try { obj.socket.send(JSON.stringify(obj.webrtcoffer)); } catch (ex) { } // End of candidates, send the offer
113 } else {
114 obj.webrtcoffer.sdp += ("a=" + e.candidate.candidate + "\r\n"); // New candidate, add it to the SDP
115 }
@@ -177,23 +177,25 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
177 obj.send = function (x) {
178 //obj.debug("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + rstr2hex(x));
179 //console.log("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + ((typeof x == 'string')?x:rstr2hex(x)));
180 - if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
181 - if (typeof x == 'string') {
182 - if (obj.debugmode == 1) {
183 - var b = new Uint8Array(x.length), c = [];
184 - for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); c.push(x.charCodeAt(i)); }
185 - if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
186 - //console.log('Send', c);
180 + try {
181 + if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
182 + if (typeof x == 'string') {
183 + if (obj.debugmode == 1) {
184 + var b = new Uint8Array(x.length), c = [];
185 + for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); c.push(x.charCodeAt(i)); }
186 + if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
187 + //console.log('Send', c);
188 + } else {
189 + var b = new Uint8Array(x.length);
190 + for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
191 + if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
192 + }
193 } else {
188 - var b = new Uint8Array(x.length);
189 - for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
190 - if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
194 + //if (obj.debugmode == 1) { console.log('Send', x); }
195 + if (obj.webRtcActive == true) { obj.webchannel.send(x); } else { obj.socket.send(x); }
196 }
192 - } else {
193 - //if (obj.debugmode == 1) { console.log('Send', x); }
194 - if (obj.webRtcActive == true) { obj.webchannel.send(x); } else { obj.socket.send(x); }
197 }
196 - }
198 + } catch (ex) { }
199 }
200
201 obj.xxOnSocketClosed = function () {
public/scripts/amt-desktop-0.0.2.js
+1
@@ -8,6 +8,7 @@
8 var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
9 var obj = {};
10 obj.canvasid = divid;
11 + obj.CanvasId = Q(divid);
12 obj.scrolldiv = scrolldiv;
13 obj.canvas = Q(divid).getContext("2d");
14 obj.protocol = 2; // KVM
views/default.handlebars
+226 -38
@@ -115,6 +115,12 @@
115 <input id=SearchInput type=text style=width:120px placeholder=Search onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
116 <input type=checkbox id=HostnameCheckBox onclick=onHostnameCheckBox() /><span title="Show device hostnames">Hostname</span>
117 </div>
118 + <div id=kvmListToolbar class=style14 style=height:100%;float:left>
119 + &nbsp;&nbsp;<input type="button" onclick="connectAllKvmFunction();" value="Connect All" />&nbsp;
120 + <input type="button" onclick="disconnectAllKvmFunction();" value="Disconnect All" />&nbsp;
121 + <input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" />AutoConnect&nbsp;
122 + <input type="button" onclick="showMultiDesktopSettings();" value="Settings" />&nbsp;
123 + </div>
124 <div id=devMapToolbar class=style14 style=height:100%;float:left>
125 &nbsp;&nbsp;<input type=text id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
126 <input type=button value=Search title="Search for location" onclick=getSearchLocation() />
@@ -128,7 +134,8 @@
134 <select id=viewselect onchange=onDeviceViewChange()>
135 <option value=1>Columns</option>
136 <option value=2>List</option>
131 - <option id=viewselectmapoption value=3>Map</option>
137 + <option value=3>Desktops</option>
138 + <option id=viewselectmapoption value=4>Map</option>
139 </select>
140 </div>
141 <div style=float:right id=devListToolbarSort>
@@ -140,6 +147,15 @@
147 </select>
148 &nbsp;
149 </div>
150 + <div style=float:right id=devListToolbarSize>
151 + Size
152 + <select id=sizeselect onchange=onDeviceViewChange()>
153 + <option value=0>Small</option>
154 + <option value=1>Medium</option>
155 + <option value=2>Large</option>
156 + </select>
157 + &nbsp;
158 + </div>
159 </div>
160 </div>
161 <div id=NoMeshesPanel style=display:none>
@@ -338,7 +354,9 @@
354 <tr id=deskarea3>
355 <td id=deskarea3x style="background:black;text-align:center;height:400px;position:relative">
356 <div id="DeskFocus" style="color:transparent;border:3px dotted rgba(255,0,0,.2);position:absolute;border-radius:5px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></div>
341 - <canvas id="Desk" width="640" height="200" style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></canvas>
357 + <div id="DeskParent">
358 + <canvas id="Desk" width="640" height="200" style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></canvas>
359 + </div>
360 </td>
361 </tr>
362 <tr id=deskarea4>
@@ -635,10 +653,12 @@
653 var nodeShortIdent = 0;
654 var desktop;
655 var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024 };
656 + var multidesktopsettings = { quality: 30, scaling: 128 };
657 var terminal;
658 var files;
659 var debugLevel = {{{debuglevel}}};
660 var features = {{{features}}};
661 + var multiDesktop = {};
662 var serverPublicNamePort = "{{{serverDnsName}}}:{{{serverPublicPort}}}";
663 var amtScanResults = null;
664 var debugmode = false;
@@ -695,7 +715,8 @@
715 meshserver.Start();
716
717 // Setup page controls
698 - document.getElementById("sortselect").selectedIndex = sort = getstore("sort", 0);
718 + Q('sortselect').selectedIndex = sort = getstore("sort", 0);
719 + Q('sizeselect').selectedIndex = getstore("viewsize", 1);
720 Q('SearchInput').value = getstore("search", "");
721 showHostnames = (getstore("showHostnames", 0) == 1);
722 Q('HostnameCheckBox').checked = showHostnames;
@@ -723,8 +744,10 @@
744 setInterval(updateDeviceTimeline, 120000); // Check every 2 minutes
745
746 // Load desktop settings
726 - t = localStorage.getItem('desktopsettings');
747 + var t = localStorage.getItem('desktopsettings');
748 if (t != null) { desktopsettings = JSON.parse(t); }
749 + t = localStorage.getItem('multidesktopsettings');
750 + if (t != null) { multidesktopsettings = JSON.parse(t); }
751 applyDesktopSettings();
752
753 // Mouse Scroll on desktop
@@ -1260,6 +1283,7 @@
1283
1284 function onDeviceViewChange() {
1285 putstore("deviceView", Q('viewselect').value);
1286 + putstore("viewsize", Q('sizeselect').value);
1287 updateDevices();
1288 }
1289
@@ -1331,26 +1355,29 @@
1355 var deviceHeaderCount;
1356 var deviceHeaders = {};
1357 function updateDevices() {
1334 - var r = "", c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value;
1335 - QV('xdevices', view < 3);
1336 - QV('xdevicesmap', view == 3);
1358 + var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value;
1359 + QV('xdevices', view < 4);
1360 + QV('xdevicesmap', view == 4);
1361 QV('devListToolbar', view < 3);
1338 - QV('devMapToolbar', view == 3);
1339 - QV('devListToolbarSort', view < 3);
1362 + QV('kvmListToolbar', view == 3);
1363 + QV('devMapToolbar', view == 4);
1364 + QV('devListToolbarSort', view < 4);
1365 + QV('devListToolbarSize', view == 3);
1366 QV('NoMeshesPanel', meshcount == 0);
1367 QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0));
1368 QV('devListToolbarSort', (meshcount != 0) && (nodes.length > 0));
1369 if ((meshcount == 0) || (nodes.length == 0)) { view = 1; }
1344 - if (view == 3) {
1370 + if (view == 4) {
1371 setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
1372 // TODO
1373 } else {
1348 - // 3 wide or list view
1374 + // 3 wide, list view or desktop view
1375 deviceHeaderId = 0;
1376 deviceHeaderCount = {};
1377 deviceHeaderTotal = 0;
1378 deviceHeaders = {};
1379 deviceHeadersTitles = {};
1380 + var kvmDivs = [];
1381
1382 // Save the list of currently checked nodeid's
1383 var checkedNodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
@@ -1361,14 +1388,15 @@
1388 if (nodes[i].v == false) continue;
1389 var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
1390 if (meshlinks == null) continue;
1391 + var meshrights = meshlinks.rights;
1392 + if ((view == 3) && (mesh2.mtype == 1)) continue;
1393 if (sort == 0) {
1394 // Mesh header
1395 if (nodes[i].meshid != current) {
1396 deviceHeaderSet();
1397 var extra = '';
1398 if (meshes[nodes[i].meshid].mtype == 1) { extra = '<span class=devHeaderx>, Intel&reg; AMT only</span>'; }
1370 - var meshrights = meshlinks.rights;
1371 - if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } r += '</tr></table>'; }
1399 + if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
1400 r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
1401 r += getMeshActions(mesh2, meshrights);
1402 r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span></div>';
@@ -1380,7 +1408,7 @@
1408 // Power header
1409 if (nodes[i].pwr !== current) {
1410 deviceHeaderSet();
1383 - if ((view == 1) && (current !== null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } r += '</tr></table>'; }
1411 + if ((view == 1) && (current !== null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
1412 r += '<div class=DevSt style=width:100%;padding-top:4px><span>' + PowerStateStr2(nodes[i].pwr) + '</span><span id=DevxHeader' + deviceHeaderId + ' class="devHeaderx"></span></div>';
1413 current = nodes[i].pwr;
1414 c = 0;
@@ -1404,12 +1432,19 @@
1432 if ((!nodes[i].conn) || (nodes[i].conn == 0)) { icon += ' gray'; }
1433 if (view == 1) {
1434 r += '<div id=devs style=display:inline-block;width:301px;height:50px;padding-top:1px;padding-bottom:1px><div style=width:22px;height:50%;float:left;padding-top:12px><input class="' + nodes[i].meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + nodes[i]._id + ' type=checkbox></div><div style=height:100%;cursor:pointer onclick=gotoDevice(\'' + nodes[i]._id + '\')><div class="i' + icon + '" style=width:50px;float:left></div><div style=height:100%><div class=g1></div><div class=e2><div class=e1 title="' + title + '">' + name + '</div><div>' + nodestate + '</div></div><div class=g2></div></div></div></div>';
1407 - } else {
1435 + } else if (view == 2) {
1436 r += '<tr><td><div id=devs class=bar18 style=height:18px;width:100%;font-size:medium>';
1437 r += '<div style=width:22px;float:left;background-color:white><input class="' + nodes[i].meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + nodes[i]._id + ' type=checkbox></div>';
1438 r += '<div style=float:left;height:18px;width:18px;background-color:white onclick=gotoDevice(\'' + nodes[i]._id + '\')><div class=j' + icon + ' style=width:16px;margin-top:1px;margin-left:2px;height:16px></div></div>';
1439 r += '<div class=g1 style=height:18px;float:left></div><div class=g2 style=height:18px;float:right></div>';
1440 r += '<div style=cursor:pointer;font-size:14px title="' + title + '" onclick=gotoDevice(\'' + nodes[i]._id + '\')><span style=float:right>' + nodestate + '</span><span style=width:300px>' + name + '</span></div></div></td></tr>';
1441 + } else if ((view == 3) && (nodes[i].conn & 1) && ((meshrights & 8) != 0)) {
1442 + r += '<div id=devs style=display:inline-block;margin:1px;background-color:lightgray;border-radius:5px;position:relative><div style=padding:3px;cursor:pointer onclick=gotoDevice(\'' + nodes[i]._id + '\',11)>';
1443 + //r += '<input class="' + nodes[i].meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + nodes[i]._id + ' type=checkbox style=float:left>';
1444 + r += '<div class="j' + icon + '" style=width:16px;float:left></div>&nbsp;' + name + '</div>';
1445 + r += '<span onclick=gotoDevice(\'' + nodes[i]._id + '\')></span><div id=xkvmid_' + nodes[i]._id.split('/')[2] + '><div id=skvmid_' + nodes[i]._id.split('/')[2] + ' style="position:absolute;color:white;left:5px;top:27px;text-shadow:0px 0px 5px #000;z-index:1000;cursor:default" onclick=toggleKvmDevice(\'' + nodes[i]._id + '\')>Disconnected</div></div>';
1446 + r += '</div>';
1447 + kvmDivs.push(nodes[i]._id);
1448 }
1449
1450 deviceHeaderTotal++;
@@ -1419,13 +1454,13 @@
1454 if ((view == 1) && (c == 2)) r += '<td><div style=width:301px></div></td>'; // Adds device padding
1455
1456 // Display all empty meshes, we need to do this because users can add devices to these at any time.
1422 - if (sort == 0 && Q('SearchInput').value == '') {
1457 + if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) {
1458 for (var i in meshes) {
1459 var mesh = meshes[i], meshlink = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
1460 if (meshlink != null) {
1461 var meshrights = meshlink.rights;
1462 if (displayedMeshes[mesh._id] == null) {
1428 - if (current != '') { r += '</tr></table>'; }
1463 + if ((current != '') && (r != '')) { r += '</tr></table>'; }
1464 r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt><span style=float:right>';
1465 r += getMeshActions(mesh, meshrights);
1466 r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></td></tr><tr>';
@@ -1462,6 +1497,132 @@
1497 for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
1498 for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
1499 p1updateInfo();
1500 +
1501 + // Take care of KVM surfaces in desktop view mode
1502 + if (view == 3) {
1503 + var vsize = [{x:180,y:101}, {x:302,y:169}, {x:454,y:255}][Q('sizeselect').selectedIndex];
1504 + for (var i in multiDesktop) { multiDesktop[i].xxdelete = true; }
1505 + for (var i in kvmDivs) {
1506 + var id = kvmDivs[i], shortid = id.split('/')[2], desk = multiDesktop[id];
1507 + if (desk != null) {
1508 + // This device already has a canvas, use it.
1509 + desk.m.CanvasId.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
1510 + Q('xkvmid_' + shortid).appendChild(desk.m.CanvasId);
1511 + delete desk.xxdelete;
1512 + QH('skvmid_' + shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][((desk.m.State == null)?desk.m.state:desk.m.State)]);
1513 + } else {
1514 + var node = getNodeFromId(id);
1515 + if ((desktopNode == node) && (desktop != null)) { // Check if the main desktop is this device, if it is, use that.
1516 + // This device already has a canvas, use it.
1517 + var c = desktop.m.CanvasId;
1518 + c.setAttribute('id', 'kvmid_' + shortid);
1519 + c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
1520 + c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
1521 + c.removeAttribute('onmousedown');
1522 + c.removeAttribute('onmouseup');
1523 + c.removeAttribute('onmousemove');
1524 + Q('xkvmid_' + shortid).appendChild(c);
1525 + QH('skvmid_' + shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][((desktop.m.State == null)?desktop.m.state:desktop.m.State)]);
1526 + if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, multidesktopsettings.quality, multidesktopsettings.scaling); }
1527 + desktop.shortid = shortid;
1528 + desktop.onStateChanged = onMultiDesktopStateChange;
1529 + multiDesktop[id] = desktop;
1530 + desktop = desktopNode = currentNode = null;
1531 + // Setup a replacement desktop
1532 + QH('DeskParent', '<canvas id="Desk" width="640" height="200" style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></canvas>');
1533 + } else {
1534 + // This is a new device, create a canvas for it.
1535 + var c = document.createElement('canvas');
1536 + c.setAttribute('id', 'kvmid_' + shortid);
1537 + c.setAttribute('width', 640);
1538 + c.setAttribute('height', 200);
1539 + c.setAttribute('oncontextmenu', 'return false');
1540 + c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
1541 + c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
1542 + Q('xkvmid_' + shortid).appendChild(c);
1543 + // Check if we need to auto-connect
1544 + if (Q('autoConnectDesktopCheckbox').checked == true) { setTimeout(function() { connectMultiDesktop(node, 1); }, 100); }
1545 + }
1546 + }
1547 + }
1548 + for (var i in multiDesktop) {
1549 + // If a device is no longer viewed, disconnect it.
1550 + if (multiDesktop[i].xxdelete == true) { multiDesktop[i].Stop(); delete multiDesktop[i]; }
1551 + }
1552 + } else {
1553 + disconnectAllKvmFunction();
1554 + Q('autoConnectDesktopCheckbox').checked = false;
1555 + }
1556 +
1557 + }
1558 + }
1559 +
1560 + function toggleKvmDevice(nodeid) {
1561 + var node = getNodeFromId(nodeid), mesh = meshes[node.meshid], meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
1562 + if ((meshrights & 8) != 0) { // Requires remote control rights
1563 + //var conn = 0;
1564 + //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
1565 + if (node.conn & 1) { connectMultiDesktop(node, 1); }
1566 + }
1567 + }
1568 +
1569 + function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
1570 + function connectAllKvmFunction() { for (var i in nodes) { if (multiDesktop[nodes[i]._id] == null) { toggleKvmDevice(nodes[i]._id); } } }
1571 + function disconnectAllKvmFunction() { for (var nodeid in multiDesktop) { multiDesktop[nodeid].Stop(); } multiDesktop = {}; }
1572 + function onMultiDesktopStateChange(desk, state) { try { QH('skvmid_' + desk.shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][state]); } catch (ex) {} }
1573 +
1574 + function showMultiDesktopSettings() {
1575 + QV('d7amtkvm', false);
1576 + QV('d7meshkvm', true);
1577 + d7bitmapquality.value = multidesktopsettings.quality;
1578 + d7bitmapscaling.value = multidesktopsettings.scaling;
1579 + setDialogMode(7, "Remote Desktop Settings", 3, showMultiDesktopSettingsChanged);
1580 + }
1581 +
1582 + function showMultiDesktopSettingsChanged() {
1583 + multidesktopsettings.quality = d7bitmapquality.value;
1584 + multidesktopsettings.scaling = d7bitmapscaling.value;
1585 + localStorage.setItem('multidesktopsettings', JSON.stringify(multidesktopsettings));
1586 + // Make changes to all current connections
1587 + for (var i in multiDesktop) { multiDesktop[i].m.SendCompressionLevel(1,multidesktopsettings.quality,multidesktopsettings.scaling); }
1588 + }
1589 +
1590 + function connectMultiDesktop(node, contype) {
1591 + var nodeid = node._id;
1592 + var desk = multiDesktop[nodeid];
1593 + if (desk == null) {
1594 + if (contype == 2) {
1595 + // Setup the Intel AMT remote desktop
1596 + if ((node.intelamt.user == null) || (node.intelamt.user == '')) { return; }
1597 + desk = CreateAmtRedirect(CreateAmtRemoteDesktop('kvmid_' + nodeid.split('/')[2]));
1598 + desk.shortid = nodeid.split('/')[2];
1599 + //desk.debugmode = debugmode;
1600 + desk.onStateChanged = onMultiDesktopStateChange;
1601 + desk.m.bpp = 1;
1602 + desk.m.useZRLE = true;
1603 + desk.m.showmouse = true;
1604 + //desk.m.onScreenSizeChange = deskAdjust;
1605 + desk.Start(nodeid, 16994, '*', '*', 0);
1606 + desk.contype = 2;
1607 + multiDesktop[nodeid] = desk;
1608 + } else if (contype == 1) {
1609 + // Setup the Mesh Agent remote desktop
1610 + desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + nodeid.split('/')[2]), serverPublicNamePort);
1611 + desk.shortid = nodeid.split('/')[2];
1612 + desk.attemptWebRTC = attemptWebRTC;
1613 + desk.onStateChanged = onMultiDesktopStateChange;
1614 + desk.m.CompressionLevel = multidesktopsettings.quality;
1615 + desk.m.ScalingLevel = multidesktopsettings.scaling;
1616 + //desk.m.onDisplayinfo = deskDisplayInfo;
1617 + //desk.m.onScreenSizeChange = deskAdjust;
1618 + desk.Start(nodeid);
1619 + desk.contype = 1;
1620 + multiDesktop[nodeid] = desk;
1621 + }
1622 + } else {
1623 + // Disconnect and clean up the remote desktop
1624 + desk.Stop();
1625 + delete multiDesktop[nodeid];
1626 }
1627 }
1628
@@ -1546,8 +1707,9 @@
1707 function addCiraDeviceToMesh(meshid) {
1708 if (xxdialogMode) return;
1709 var mesh = meshes[meshid];
1549 - var meshidx = meshid.substring(5);
1550 - if (meshidx[0] == '/') meshidx = meshidx.substring(1);
1710 +
1711 + // Replace non alphabetic characters (@ and $) with 'X' because MPS username cannot accept it.
1712 + var meshidx = meshid.split('/')[2].replace(/\@/g, 'X').replace(/\$/g, 'X');
1713
1714 var y = '<select id=dlgAddCiraSel onclick=dlgAddCiraSelClick() style=width:230px><option value=0>MeshCommander Script</option><option value=1>Manual Username/Password</option>';
1715 if ((features & 16) == 0) { y += '<option value=2>Manual Certificate</option></select>'; } // Only display this option if Intel AMT CIRA with Mutual-Auth is allowed.
@@ -1601,8 +1763,6 @@
1763 function inviteAgentToMesh(meshid) {
1764 if (xxdialogMode) return;
1765 var mesh = meshes[meshid];
1604 - var meshidx = meshid.substring(5);
1605 - if (meshidx[0] == '/') meshidx = meshidx.substring(1);
1766 var x = "Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for " + EscapeHtml(mesh.name) + ".<br /><br />";
1767 x += addHtmlValue('E-Mail', '<input id=agentInviteEmail style=width:240px onkeyup=validateAgentInvite()></input>');
1768 setDialogMode(2, "Invite Mesh Agent", 3, performAgentInvite, x, meshid);
@@ -1619,10 +1779,7 @@
1779
1780 function addAgentToMesh(meshid) {
1781 if (xxdialogMode) return;
1622 - var mesh = meshes[meshid];
1623 - var meshidx = meshid.substring(5);
1624 - if (meshidx[0] == '/') meshidx = meshidx.substring(1);
1625 - var x = "";
1782 + var mesh = meshes[meshid], x = '';
1783 x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=2>Windows (UnInstall)</option><option value=3>Linux (UnInstall)</option></select>') + '<hr>';
1784
1785 // Windows agent install
@@ -2459,8 +2616,7 @@
2616 }
2617
2618 function getNodeRights(nodeid) {
2462 - var node = getNodeFromId(nodeid);
2463 - var mesh = meshes[node.meshid];
2619 + var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
2620 return mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
2621 }
2622
@@ -2471,6 +2627,7 @@
2627 var powerTimeline = null;
2628 function getCurrentNode() { return currentNode; };
2629 function gotoDevice(nodeid, panel, refresh) {
2630 + //disconnectAllKvmFunction();
2631 var node = getNodeFromId(nodeid);
2632 var mesh = meshes[node.meshid];
2633 var meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
@@ -2607,7 +2764,6 @@
2764 Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
2765
2766 // Setup/Refresh the desktop tab
2610 - setupDesktop();
2767 setupTerminal();
2768 setupFiles();
2769 var consoleRights = ((meshrights & 16) != 0);
@@ -2640,6 +2796,7 @@
2796 // Request the power timeline
2797 if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
2798 }
2799 + setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
2800 if (!panel) panel = 10;
2801 go(panel);
2802 }
@@ -2917,7 +3074,7 @@
3074 var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc'];
3075 function showEditNodeValueDialog(mode) {
3076 if (xxdialogMode) return;
2920 - var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:230px maxlength=32 onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />');
3077 + var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:230px maxlength=64 onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />');
3078 setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
3079 var v = currentNode[showEditNodeValueDialog_modes2[mode]];
3080 if (v == null) v = '';
@@ -2945,7 +3102,34 @@
3102 var desktopNode;
3103 function setupDesktop() {
3104 // Setup the remote desktop
2948 - if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); delete desktop; desktop = null; }
3105 + if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); delete desktop; desktopNode = null; desktop = null; }
3106 +
3107 + // If the device desktop is already connected in multi-desktop, use that.
3108 + if ((desktopNode != currentNode) || (desktop == null)) {
3109 + var xdesk = multiDesktop[currentNode._id];
3110 + if (xdesk != null) {
3111 + // This device already has a canvas, use it.
3112 + QH('DeskParent', '');
3113 + var c = xdesk.m.CanvasId;
3114 + c.setAttribute('id', 'Desk');
3115 + c.setAttribute('style', 'width:100%;-ms-touch-action:none;margin-left:0px');
3116 + c.setAttribute('onmousedown', 'dmousedown(event)');
3117 + c.setAttribute('onmouseup', 'dmouseup(event)');
3118 + c.setAttribute('onmousemove', 'dmousemove(event)');
3119 + c.removeAttribute('onclick');
3120 + Q('DeskParent').appendChild(c);
3121 + desktop = xdesk;
3122 + if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling); }
3123 + desktop.onStateChanged = onDesktopStateChange;
3124 + desktopNode = currentNode;
3125 + onDesktopStateChange(desktop, desktop.State);
3126 + delete multiDesktop[currentNode._id];
3127 + } else {
3128 + // Device is not already connected, just setup a blank canvas
3129 + QH('DeskParent', '<canvas id="Desk" width="640" height="200" style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></canvas>');
3130 + desktopNode = currentNode;
3131 + }
3132 + }
3133 desktopNode = currentNode;
3134 updateDesktopButtons();
3135
@@ -2955,22 +3139,22 @@
3139
3140 // Show and enable the right buttons
3141 function updateDesktopButtons() {
2958 - var mesh = meshes[desktopNode.meshid];
3142 + var mesh = meshes[currentNode.meshid];
3143 var deskState = ((desktop != null) && (desktop.state != 0));
3144
3145 // Show the right buttons
3146 QV('disconnectbutton1span', (deskState == true));
3147 QV('connectbutton1span', (deskState == false) && (mesh.mtype == 2));
2964 - QV('connectbutton1hspan', (deskState == false) && (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))));
3148 + QV('connectbutton1hspan', (deskState == false) && (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))));
3149
3150 // Show the right settings
2967 - QV('d7amtkvm', (desktopNode.intelamt != null && ((desktopNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
3151 + QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
3152 QV('d7meshkvm', (mesh.mtype == 2) && ((deskState == false) || (desktop.contype == 1)));
3153
3154 // Enable buttons
2971 - var online = ((desktopNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
3155 + var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
3156 QE('connectbutton1', online);
2973 - var hwonline = ((desktopNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
3157 + var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
3158 QE('connectbutton1h', hwonline);
3159 }
3160
@@ -2980,6 +3164,7 @@
3164
3165 function connectDesktop(e, contype) {
3166 if (desktop == null) {
3167 + desktopNode = currentNode;
3168 if (contype == 2) {
3169 // Setup the Intel AMT remote desktop
3170 if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
@@ -3010,7 +3195,7 @@
3195 // Disconnect and clean up the remote desktop
3196 desktop.Stop();
3197 delete desktop;
3013 - desktop = null;
3198 + desktopNode = desktop = null;
3199 }
3200 }
3201
@@ -3029,7 +3214,7 @@
3214 // Disconnect and clean up the remote desktop
3215 desktop.Stop();
3216 delete desktop;
3032 - desktop = null;
3217 + desktopNode = desktop = null;
3218 QV('DeskFocus', false);
3219 QV('termdisplays', false);
3220 deskFocusBtn.value = 'All Focus';
@@ -3044,6 +3229,8 @@
3229 }
3230
3231 function showDesktopSettings() {
3232 + applyDesktopSettings();
3233 + updateDesktopButtons();
3234 setDialogMode(7, "Remote Desktop Settings", 3, showDesktopSettingsChanged);
3235 }
3236
@@ -3074,7 +3261,7 @@
3261 d7showfocus.checked = desktopsettings.showfocus;
3262 d7showcursor.checked = desktopsettings.showmouse;
3263 d7bitmapquality.value = 40; // Default value
3077 - if (ops.indexOf(desktopsettings.quality) >= 0) { d7bitmapquality.value = desktopsettings.quality; }
3264 + if (ops.indexOf(parseInt(desktopsettings.quality)) >= 0) { d7bitmapquality.value = desktopsettings.quality; }
3265 d7bitmapscaling.value = desktopsettings.scaling;
3266 QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
3267 }
@@ -4925,6 +5112,7 @@
5112 QS('MainDevConsole').backgroundColor = ((x == 15) ? "#003366" : "#808080");
5113 QV('MeshSubMenuSpan', x >= 20 && x < 30);
5114 QS('MeshGeneral').backgroundColor = ((x == 20) ? "#003366" : "#808080");
5115 + if (x == 1) updateDevices();
5116 }
5117
5118 // Generic methods