fix automatic clipboard not working #7260

Signed-off-by: si458 <simonsmith5521@gmail.com>

si458 committed Mar 24, 2026 at 10:26 UTC 5f8b74fa89fb9be8625de72f9b1d2a64f97a60a0
3 files changed +80 -30
agents/meshcore.js
+2 -2
@@ -1590,14 +1590,14 @@ function handleServerCommand(data) {
1590 if (require('MeshAgent').isService) {
1591 require('clipboard').dispatchRead().then(function (str) {
1592 if (str) {
1593 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1593 + if (data.tag != 3) { MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data); }
1594 mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1595 }
1596 });
1597 } else {
1598 require('clipboard').read().then(function (str) {
1599 if (str) {
1600 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1600 + if (data.tag != 3) { MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data); }
1601 mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1602 }
1603 });
views/default.handlebars
+39 -14
@@ -1616,6 +1616,7 @@
1616 var deskKeyboardShortcuts = [];
1617 var deskKeyboardStrings = [];
1618 var deskLastClipboardSent = null;
1619 + var deskLastClipboardReceived = null;
1620 var requestedLastConnects = false;
1621 var devicePagingState = null;
1622
@@ -2781,8 +2782,13 @@
2782 } else if ((message.type === 'getclip') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2783 if ((message.tag == 1) && (xxdialogTag === 'clipboard')) {
2784 Q('d2clipText').value = message.data; // Put remote clipboard data into dialog box
2784 - } else if (message.tag === 2) {
2785 - if (navigator.clipboard != null) { navigator.clipboard.writeText(message.data).then(function() { }).catch(function(err) { console.log(err); }) } // Put remote clipboard data into our clipboard
2785 + } else if ((message.tag === 2) || (message.tag === 3)) {
2786 + // tag 2 = manual clipboard-in button, tag 3 = auto-clipboard poll
2787 + if ((navigator.clipboard != null) && (message.data != deskLastClipboardReceived) && document.hasFocus()) {
2788 + deskLastClipboardReceived = message.data;
2789 + deskLastClipboardSent = message.data; // prevent echo back to remote
2790 + navigator.clipboard.writeText(message.data).then(function() { }).catch(function(err) { });
2791 + }
2792 }
2793 } else if ((message.type === 'setclip') && (xxdialogTag === 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2794 // Display success/fail on the clipboard dialog box.
@@ -9769,7 +9775,15 @@
9775 case 3:
9776 if (desktop && (desktop.serverIsRecording == true)) { QV('deskRecordIcon', true); }
9777 desktop.startTime = new Date();
9772 - deskLastClipboardSent = null;
9778 + deskLastClipboardReceived = null;
9779 + // Pre-seed sent value with current local clipboard so we don't blast it to the remote on connect
9780 + if ((navigator.clipboard != null) && (navigator.clipboard.readText != null) && (Mstsc.browser() != 'firefox')) {
9781 + try {
9782 + navigator.clipboard.readText().then(function(text) { deskLastClipboardSent = text; }).catch(function(err) { deskLastClipboardSent = null; });
9783 + } catch (ex) { deskLastClipboardSent = null; }
9784 + } else {
9785 + deskLastClipboardSent = null;
9786 + }
9787 if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
9788 break;
9789 default:
@@ -9791,17 +9805,28 @@
9805 QH('DeskTimer', zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
9806 QH('DeskLatency', latencyStr);
9807 // Auto-clipboard
9794 - if ((((desktop.contype != 4) && (desktopsettings.autoclipboard === true)) || ((desktop.contype == 4) && (desktopsettings.rdpautoclipboard === true))) && (navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
9795 - if (Mstsc.browser() == 'firefox') return; // this is needed because firefox pops up a PASTE option every second which is annoying
9796 - try {
9797 - navigator.clipboard.readText().then(function(text) {
9798 - if (desktop == null) return;
9799 - if ((text != null) && (deskLastClipboardSent != text)) {
9800 - if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { console.log('s3'); meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
9801 - deskLastClipboardSent = text;
9802 - }
9803 - }).catch(function(err) { });
9804 - } catch (ex) { console.log(ex); }
9808 + var autoClipEnabled = ((desktop.contype != 4) && (desktopsettings.autoclipboard === true)) || ((desktop.contype == 4) && (desktopsettings.rdpautoclipboard === true));
9809 + if (autoClipEnabled && (navigator.clipboard != null)) {
9810 + // Local -> Remote: skip on Firefox as readText triggers an annoying paste popup every second
9811 + if ((Mstsc.browser() != 'firefox') && (navigator.clipboard.readText != null)) {
9812 + try {
9813 + navigator.clipboard.readText().then(function(text) {
9814 + if (desktop == null) return;
9815 + if ((text != null) && (deskLastClipboardSent != text)) {
9816 + if (desktop.m.setClipboard) {
9817 + desktop.m.setClipboard(text);
9818 + } else {
9819 + meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
9820 + }
9821 + deskLastClipboardSent = text;
9822 + }
9823 + }).catch(function(err) { });
9824 + } catch (ex) { console.log(ex); }
9825 + }
9826 + // Remote -> Local: poll remote clipboard every second (non-RDP only; RDP uses onClipboardChanged callback)
9827 + if ((desktop.contype != 4) && (currentNode != null) && (navigator.clipboard.writeText != null)) {
9828 + meshserver.send({ action: 'msg', type: 'getclip', nodeid: currentNode._id, tag: 3 });
9829 + }
9830 }
9831 } else {
9832 QH('DeskTimer', '');
views/default3.handlebars
+39 -14
@@ -2132,6 +2132,7 @@
2132 var deskKeyboardShortcuts = [];
2133 var deskKeyboardStrings = [];
2134 var deskLastClipboardSent = null;
2135 + var deskLastClipboardReceived = null;
2136 var requestedLastConnects = false;
2137 var devicePagingState = null;
2138
@@ -3453,8 +3454,13 @@
3454 } else if ((message.type === 'getclip') && (currentNode != null) && (currentNode._id == message.nodeid)) {
3455 if ((message.tag == 1) && (xxdialogTag === 'clipboard')) {
3456 Q('d2clipText').value = message.data; // Put remote clipboard data into dialog box
3456 - } else if (message.tag === 2) {
3457 - if (navigator.clipboard != null) { navigator.clipboard.writeText(message.data).then(function () { }).catch(function (err) { console.log(err); }) } // Put remote clipboard data into our clipboard
3457 + } else if ((message.tag === 2) || (message.tag === 3)) {
3458 + // tag 2 = manual clipboard-in button, tag 3 = auto-clipboard poll
3459 + if ((navigator.clipboard != null) && (message.data != deskLastClipboardReceived) && document.hasFocus()) {
3460 + deskLastClipboardReceived = message.data;
3461 + deskLastClipboardSent = message.data; // prevent echo back to remote
3462 + navigator.clipboard.writeText(message.data).then(function () { }).catch(function (err) { });
3463 + }
3464 }
3465 } else if ((message.type === 'setclip') && (xxdialogTag === 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
3466 // Display success/fail on the clipboard dialog box.
@@ -10747,7 +10753,15 @@
10753 case 3:
10754 if (desktop && (desktop.serverIsRecording == true)) { QV('deskRecordIcon', true); }
10755 desktop.startTime = new Date();
10750 - deskLastClipboardSent = null;
10756 + deskLastClipboardReceived = null;
10757 + // Pre-seed sent value with current local clipboard so we don't blast it to the remote on connect
10758 + if ((navigator.clipboard != null) && (navigator.clipboard.readText != null) && (Mstsc.browser() != 'firefox')) {
10759 + try {
10760 + navigator.clipboard.readText().then(function (text) { deskLastClipboardSent = text; }).catch(function (err) { deskLastClipboardSent = null; });
10761 + } catch (ex) { deskLastClipboardSent = null; }
10762 + } else {
10763 + deskLastClipboardSent = null;
10764 + }
10765 if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
10766 break;
10767 default:
@@ -10769,17 +10783,28 @@
10783 QH('DeskTimer', zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
10784 QH('DeskLatency', latencyStr);
10785 // Auto-clipboard
10772 - if ((((desktop.contype != 4) && (desktopsettings.autoclipboard === true)) || ((desktop.contype == 4) && (desktopsettings.rdpautoclipboard === true))) && (navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
10773 - if (Mstsc.browser() == 'firefox') return; // this is needed because firefox pops up a PASTE option every second which is annoying
10774 - try {
10775 - navigator.clipboard.readText().then(function (text) {
10776 - if (desktop == null) return;
10777 - if ((text != null) && (deskLastClipboardSent != text)) {
10778 - if (desktop.m.setClipboard) { desktop.m.setClipboard(text); } else { console.log('s3'); meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }
10779 - deskLastClipboardSent = text;
10780 - }
10781 - }).catch(function (err) { });
10782 - } catch (ex) { console.log(ex); }
10786 + var autoClipEnabled = ((desktop.contype != 4) && (desktopsettings.autoclipboard === true)) || ((desktop.contype == 4) && (desktopsettings.rdpautoclipboard === true));
10787 + if (autoClipEnabled && (navigator.clipboard != null)) {
10788 + // Local -> Remote: skip on Firefox as readText triggers an annoying paste popup every second
10789 + if ((Mstsc.browser() != 'firefox') && (navigator.clipboard.readText != null)) {
10790 + try {
10791 + navigator.clipboard.readText().then(function (text) {
10792 + if (desktop == null) return;
10793 + if ((text != null) && (deskLastClipboardSent != text)) {
10794 + if (desktop.m.setClipboard) {
10795 + desktop.m.setClipboard(text);
10796 + } else {
10797 + meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
10798 + }
10799 + deskLastClipboardSent = text;
10800 + }
10801 + }).catch(function (err) { });
10802 + } catch (ex) { console.log(ex); }
10803 + }
10804 + // Remote -> Local: poll remote clipboard every second (non-RDP only; RDP uses onClipboardChanged callback)
10805 + if ((desktop.contype != 4) && (currentNode != null) && (navigator.clipboard.writeText != null)) {
10806 + meshserver.send({ action: 'msg', type: 'getclip', nodeid: currentNode._id, tag: 3 });
10807 + }
10808 }
10809 } else {
10810 QH('DeskTimer', '');