Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automagical copy paste #1174

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 173 additions & 3 deletions app/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const UI = {
isSafari: false,
lastKeyboardinput: null,
defaultKeyboardinputLen: 100,
needToCheckClipboardChange: false,

inhibit_reconnect: true,
reconnect_callback: null,
Expand Down Expand Up @@ -936,24 +937,180 @@ const UI = {
}
},

readClipboard(callback) {
var readfuture = navigator.clipboard.readText();
readfuture
.then(text => callback(text))
.catch(() =>
Log.Debug("Failed to read system clipboard")
);
},

// Copy text from NoVNC desktop to local computer
clipboardReceive(e) {
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
Log.Debug("<< UI.clipboardReceive");
var curvalue = document.getElementById('noVNC_clipboard_text').value;
if (curvalue != e.detail.text) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👎

We should always update the local clipboard on a proper event.

Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
Log.Debug("<< UI.clipboardReceive");

var writefuture = navigator.clipboard.writeText(e.detail.text);
writefuture
.then(function() {
/* clipboard successfully set */
UI.popupMessage("Selection Copied");
}, function() {
console.error("Failed to write system clipboard (trying to copy from NoVNC clipboard)")
});
}
},

popupMessage(msg) {
// Quick popup to give feedback that selection was copied
setTimeout(UI.showOverlay.bind(this, msg, 500), 200);
},

// Enter and focus events come when we return to NoVNC.
// In both cases, check the local clipboard to see if it changed.
focusVNC() {
UI.copyFromLocalClipboard();
},
enterVNC() {
UI.copyFromLocalClipboard();
},
copyFromLocalClipboard() {
UI.readClipboard(text => {
var clipVal = document.getElementById('noVNC_clipboard_text').value;
if ( clipVal != text ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is essential, otherwise we have no mechanism that avoids just blindly overwriting the server clipboard whenever focus changes.

However this is still a hack, so I think we should reference the clipboardchange event and that we're waiting for browsers to implement it.

document.getElementById('noVNC_clipboard_text').value = text;
UI.popupMessage("Copied from Local Clipboard");
UI.rfb.clipboardPasteFrom(text);
}
// Reset flag to prevent checking too often
UI.needToCheckClipboardChange = false;
})
},

// These 3 events indicate the focus has gone outside the NoVNC.
// When outside the NoVNC, the system clipboard could change.
leaveVNC() {
UI.needToCheckClipboardChange = true;
},
blurVNC() {
UI.needToCheckClipboardChange = true;
},
focusoutVNC() {
UI.needToCheckClipboardChange = true;
},

// On these 2 events, check if we need to look at clipboard.
mouseMoveVNC() {
if ( UI.needToCheckClipboardChange ) {
UI.copyFromLocalClipboard();
}
},
mouseDownVNC() {
if ( UI.needToCheckClipboardChange ) {
UI.copyFromLocalClipboard();
}
},

clipboardClear() {
document.getElementById('noVNC_clipboard_text').value = "";
UI.popupMessage("Clipboard Cleared");
// TODO: Should this also clear the local clipboard?
UI.rfb.clipboardPasteFrom("");
},

// Send clipboard from HTML clipboard element to NoVNC desktop
clipboardSend() {
const text = document.getElementById('noVNC_clipboard_text').value;
Log.Debug(">> UI.clipboardSend: " + text.substr(0, 40) + "...");
UI.rfb.clipboardPasteFrom(text);
Log.Debug("<< UI.clipboardSend");
},

/**
* Show the terminal overlay for a given amount of time.
*
* The terminal overlay appears in inverse video in a large font, centered
* over the terminal. You should probably keep the overlay message brief,
* since it's in a large font and you probably aren't going to check the size
* of the terminal first.
*
* @param {string} msg The text (not HTML) message to display in the overlay.
* @param {number} opt_timeout The amount of time to wait before fading out
* the overlay. Defaults to 1.5 seconds. Pass null to have the overlay
* stay up forever (or until the next overlay).
*/
showOverlay(msg, opt_timeout) {
if (!UI.overlayNode) {
UI.overlayNode = document.createElement('div');
UI.overlayNode.style.cssText = (
'border-radius: 15px;' +
'font-size: xx-large;' +
'opacity: 0.90;' +
'padding: 0.2em 0.5em 0.2em 0.5em;' +
'position: absolute;' +
'-webkit-user-select: none;' +
'-webkit-transition: opacity 180ms ease-in;' +
'-moz-user-select: none;' +
'-moz-transition: opacity 180ms ease-in;');
UI.overlayNode.style.color = 'rgb(16,16,16)';
UI.overlayNode.style.backgroundColor = 'rgb(240,240,240)';
UI.overlayNode.style.fontFamily = '"DejaVu Sans Mono", "Noto Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace"';
UI.overlayNode.style.opacity = '0.90';

//UI.overlayNode.addEventListener('mousedown', function(e) {
// e.preventDefault();
// e.stopPropagation();
// }, true);
}

UI.overlayNode.textContent = msg;

if (!UI.overlayNode.parentNode_) {
UI.overlayNode.parentNode_ = document.getElementById('noVNC_container');
}
UI.overlayNode.parentNode_.appendChild(UI.overlayNode);

var divWidth = UI.overlayNode.parentNode_.offsetWidth;
var divHeight = UI.overlayNode.parentNode_.offsetHeight;
var overlayWidth = UI.overlayNode.offsetWidth;
var overlayHeight = UI.overlayNode.offsetHeight;

UI.overlayNode.style.top =
(divHeight - overlayHeight) / 2 + 'px';
UI.overlayNode.style.left =
(divWidth - overlayWidth) / 2 + 'px';

if (UI.overlayTimeout)
clearTimeout(UI.overlayTimeout);

if (opt_timeout === null)
opt_timeout = 500;

UI.overlayTimeout = setTimeout(() => {
UI.overlayNode.style.opacity = '0';
UI.overlayTimeout = setTimeout(() => UI.hideOverlay(), 200);
}, opt_timeout || 1500);
},

/**
* Hide the terminal overlay immediately.
*
* Useful when we show an overlay for an event with an unknown end time.
*/
hideOverlay() {
if (UI.overlayTimeout)
clearTimeout(UI.overlayTimeout);
UI.overlayTimeout = null;

if (UI.overlayNode.parentNode_)
UI.overlayNode.parentNode_.removeChild(UI.overlayNode);
UI.overlayNode.style.opacity = '0.90';
},

/* ------^-------
* /CLIPBOARD
* ==============
Expand Down Expand Up @@ -1024,6 +1181,15 @@ const UI = {
UI.rfb.addEventListener("securityfailure", UI.securityFailed);
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);

document.addEventListener('mouseenter', UI.enterVNC);
document.addEventListener('mouseleave', UI.leaveVNC);
document.addEventListener('blur', UI.blurVNC);
document.addEventListener('focus', UI.focusVNC);
document.addEventListener('focusout', UI.focusoutVNC);
document.addEventListener('mousemove', UI.mouseMoveVNC);
document.addEventListener('mousedown', UI.mouseDownVNC);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need all of these events? Isn't it sufficient with just focus?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discovered while implementing the clipboard feature that the Chrome API clipboard.readText() is still buggy. It fails sometimes (not-deterministic why) with undefined error. When using all these events here to try to read it, it mostly works. I reported the bug to the Chrome community and suggest not implement this feature until it doesn't work properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's disappointing. Do you have a link?

Copy link

@akamos akamos Apr 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


UI.rfb.addEventListener("bell", UI.bell);
UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
UI.rfb.clipViewport = UI.getSetting('view_clip');
Expand Down Expand Up @@ -1373,6 +1539,10 @@ const UI = {
keepVirtualKeyboard(event) {
const input = document.getElementById('noVNC_keyboardinput');

if ( UI.needToCheckClipboardChange ) {
UI.copyFromLocalClipboard();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't hijack existing (and unrelated) event handlers.


// Only prevent focus change if the virtual keyboard is active
if (document.activeElement != input) {
return;
Expand Down
21 changes: 0 additions & 21 deletions core/input/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,6 @@ export default class Keyboard {
return;
}

// Alt behaves more like AltGraph on macOS, so shuffle the
// keys around a bit to make things more sane for the remote
// server. This method is used by RealVNC and TigerVNC (and
// possibly others).
if (browser.isMac()) {
switch (keysym) {
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
}
}

// Is this key already pressed? If so, then we must use the
// same keysym or we'll confuse the server
if (code in this._keyDownList) {
Expand Down