From 44db9c92e863822524e31d76a61d3be2e34151b9 Mon Sep 17 00:00:00 2001 From: Giannis Kosmas Date: Fri, 17 Nov 2017 21:51:41 +0200 Subject: [PATCH 001/764] Greek translations revised --- app/locale/el.json | 13 +++--- po/el.po | 109 +++++++++++++++++++++++++-------------------- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/app/locale/el.json b/app/locale/el.json index 669625ef..f801251c 100644 --- a/app/locale/el.json +++ b/app/locale/el.json @@ -1,13 +1,16 @@ { "Connecting...": "Συνδέεται...", + "Disconnecting...": "Aποσυνδέεται...", + "Reconnecting...": "Επανασυνδέεται...", + "Internal error": "Εσωτερικό σφάλμα", + "Must set host": "Πρέπει να οριστεί ο διακομιστής", "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ", "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ", - "Disconnecting...": "Aποσυνδέεται...", + "Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε", "Disconnected": "Αποσυνδέθηκε", - "Must set host": "Πρέπει να οριστεί ο διακομιστής", - "Reconnecting...": "Επανασυνδέεται...", + "New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ", + "New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ", "Password is required": "Απαιτείται ο κωδικός πρόσβασης", - "Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης", "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:", "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου", "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου", @@ -47,10 +50,8 @@ "Scaling Mode:": "Λειτουργία Κλιμάκωσης:", "None": "Καμία", "Local Scaling": "Τοπική Κλιμάκωση", - "Local Downscaling": "Τοπική Συρρίκνωση", "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους", "Advanced": "Για προχωρημένους", - "Local Cursor": "Τοπικός Δρομέας", "Repeater ID:": "Repeater ID:", "WebSocket": "WebSocket", "Encrypt": "Κρυπτογράφηση", diff --git a/po/el.po b/po/el.po index 34a173eb..a9815545 100644 --- a/po/el.po +++ b/po/el.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: noVNC 0.6.1\n" "Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-10-06 10:07+0200\n" +"POT-Creation-Date: 2017-11-17 21:40+0200\n" "PO-Revision-Date: 2017-10-11 16:16+0200\n" "Last-Translator: Giannis Kosmas \n" "Language-Team: none\n" @@ -17,42 +17,54 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: ../app/ui.js:430 +#: ../app/ui.js:404 msgid "Connecting..." msgstr "Συνδέεται..." -#: ../app/ui.js:438 -msgid "Connected (encrypted) to " -msgstr "Συνδέθηκε (κρυπτογραφημένα) με το " - -#: ../app/ui.js:440 -msgid "Connected (unencrypted) to " -msgstr "Συνδέθηκε (μη κρυπτογραφημένα) με το " - -#: ../app/ui.js:446 +#: ../app/ui.js:411 msgid "Disconnecting..." msgstr "Aποσυνδέεται..." -#: ../app/ui.js:450 -msgid "Disconnected" -msgstr "Αποσυνδέθηκε" - -#: ../app/ui.js:1052 ../core/rfb.js:248 -msgid "Must set host" -msgstr "Πρέπει να οριστεί ο διακομιστής" - -#: ../app/ui.js:1101 +#: ../app/ui.js:417 msgid "Reconnecting..." msgstr "Επανασυνδέεται..." -#: ../app/ui.js:1140 +#: ../app/ui.js:422 +msgid "Internal error" +msgstr "Εσωτερικό σφάλμα" + +#: ../app/ui.js:1019 +msgid "Must set host" +msgstr "Πρέπει να οριστεί ο διακομιστής" + +#: ../app/ui.js:1099 +msgid "Connected (encrypted) to " +msgstr "Συνδέθηκε (κρυπτογραφημένα) με το " + +#: ../app/ui.js:1101 +msgid "Connected (unencrypted) to " +msgstr "Συνδέθηκε (μη κρυπτογραφημένα) με το " + +#: ../app/ui.js:1119 +msgid "Something went wrong, connection is closed" +msgstr "Κάτι πήγε στραβά, η σύνδεση διακόπηκε" + +#: ../app/ui.js:1129 +msgid "Disconnected" +msgstr "Αποσυνδέθηκε" + +#: ../app/ui.js:1142 +msgid "New connection has been rejected with reason: " +msgstr "Η νέα σύνδεση απορρίφθηκε διότι: " + +#: ../app/ui.js:1145 +msgid "New connection has been rejected" +msgstr "Η νέα σύνδεση απορρίφθηκε " + +#: ../app/ui.js:1166 msgid "Password is required" msgstr "Απαιτείται ο κωδικός πρόσβασης" -#: ../core/rfb.js:548 -msgid "Disconnect timeout" -msgstr "Παρέλευση χρονικού ορίου αποσύνδεσης" - #: ../vnc.html:89 msgid "noVNC encountered an error:" msgstr "το noVNC αντιμετώπισε ένα σφάλμα:" @@ -210,77 +222,78 @@ msgid "Local Scaling" msgstr "Τοπική Κλιμάκωση" #: ../vnc.html:216 -msgid "Local Downscaling" -msgstr "Τοπική Συρρίκνωση" - -#: ../vnc.html:217 msgid "Remote Resizing" msgstr "Απομακρυσμένη Αλλαγή μεγέθους" -#: ../vnc.html:222 +#: ../vnc.html:221 msgid "Advanced" msgstr "Για προχωρημένους" -#: ../vnc.html:225 -msgid "Local Cursor" -msgstr "Τοπικός Δρομέας" - -#: ../vnc.html:229 +#: ../vnc.html:224 msgid "Repeater ID:" msgstr "Repeater ID:" -#: ../vnc.html:233 +#: ../vnc.html:228 msgid "WebSocket" msgstr "WebSocket" -#: ../vnc.html:236 +#: ../vnc.html:231 msgid "Encrypt" msgstr "Κρυπτογράφηση" -#: ../vnc.html:239 +#: ../vnc.html:234 msgid "Host:" msgstr "Όνομα διακομιστή:" -#: ../vnc.html:243 +#: ../vnc.html:238 msgid "Port:" msgstr "Πόρτα διακομιστή:" -#: ../vnc.html:247 +#: ../vnc.html:242 msgid "Path:" msgstr "Διαδρομή:" -#: ../vnc.html:254 +#: ../vnc.html:249 msgid "Automatic Reconnect" msgstr "Αυτόματη επανασύνδεση" -#: ../vnc.html:257 +#: ../vnc.html:252 msgid "Reconnect Delay (ms):" msgstr "Καθυστέρηση επανασύνδεσης (ms):" -#: ../vnc.html:263 +#: ../vnc.html:258 msgid "Logging:" msgstr "Καταγραφή:" -#: ../vnc.html:275 +#: ../vnc.html:270 msgid "Disconnect" msgstr "Αποσύνδεση" -#: ../vnc.html:294 +#: ../vnc.html:289 msgid "Connect" msgstr "Σύνδεση" -#: ../vnc.html:304 +#: ../vnc.html:299 msgid "Password:" msgstr "Κωδικός Πρόσβασης:" -#: ../vnc.html:318 +#: ../vnc.html:313 msgid "Cancel" msgstr "Ακύρωση" -#: ../vnc.html:334 +#: ../vnc.html:329 msgid "Canvas not supported." msgstr "Δεν υποστηρίζεται το στοιχείο Canvas" +#~ msgid "Disconnect timeout" +#~ msgstr "Παρέλευση χρονικού ορίου αποσύνδεσης" + +#~ msgid "Local Downscaling" +#~ msgstr "Τοπική Συρρίκνωση" + +#~ msgid "Local Cursor" +#~ msgstr "Τοπικός Δρομέας" + #~ msgid "" #~ "Forcing clipping mode since scrollbars aren't supported by IE in " #~ "fullscreen" From ddcb60c3b395091a6edcc4d285e8eef578b76777 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 20 Nov 2017 15:21:59 +0100 Subject: [PATCH 002/764] Don't log WebSocket close by default This is an expected event so it shouldn't be logged by default. --- core/rfb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rfb.js b/core/rfb.js index 4cd08565..41157452 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -194,7 +194,7 @@ export default function RFB(target, url, options) { } }.bind(this)); this._sock.on('close', function (e) { - Log.Warn("WebSocket on-close event"); + Log.Debug("WebSocket on-close event"); var msg = ""; if (e.code) { msg = "(code: " + e.code; From ceea51fa16f71196a770e0c71d24583350ecfcf7 Mon Sep 17 00:00:00 2001 From: Mariusz Jamro Date: Tue, 21 Nov 2017 19:56:43 +0100 Subject: [PATCH 003/764] Update polish translations to match recent code changes --- app/locale/pl.json | 13 +++--- po/pl.po | 111 +++++++++++++++++++++++++-------------------- 2 files changed, 69 insertions(+), 55 deletions(-) diff --git a/app/locale/pl.json b/app/locale/pl.json index 3033163c..006ac7a5 100644 --- a/app/locale/pl.json +++ b/app/locale/pl.json @@ -1,13 +1,16 @@ { "Connecting...": "Łączenie...", + "Disconnecting...": "Rozłączanie...", + "Reconnecting...": "Łączenie...", + "Internal error": "Błąd wewnętrzny", + "Must set host": "Host i port są wymagane", "Connected (encrypted) to ": "Połączenie (szyfrowane) z ", "Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ", - "Disconnecting...": "Rozłączanie...", + "Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte", "Disconnected": "Rozłączony", - "Must set host": "Host i port są wymagane", - "Reconnecting...": "Łączenie...", + "New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ", + "New connection has been rejected": "Nowe połączenie zostało odrzucone", "Password is required": "Hasło jest wymagane", - "Disconnect timeout": "Timeout rozłączenia", "noVNC encountered an error:": "noVNC napotkało błąd:", "Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień", "Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport", @@ -47,10 +50,8 @@ "Scaling Mode:": "Tryb Skalowania:", "None": "Brak", "Local Scaling": "Skalowanie lokalne", - "Local Downscaling": "Downscaling lokalny", "Remote Resizing": "Skalowanie zdalne", "Advanced": "Zaawansowane", - "Local Cursor": "Lokalny kursor", "Repeater ID:": "ID Repeatera:", "WebSocket": "WebSocket", "Encrypt": "Szyfrowanie", diff --git a/po/pl.po b/po/pl.po index f3e567d5..76881723 100644 --- a/po/pl.po +++ b/po/pl.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: noVNC 0.6.1\n" "Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-10-06 10:07+0200\n" -"PO-Revision-Date: 2017-10-10 11:26+0200\n" +"POT-Creation-Date: 2017-11-21 19:53+0100\n" +"PO-Revision-Date: 2017-11-21 19:54+0100\n" "Last-Translator: Mariusz Jamro \n" "Language-Team: Polish\n" "Language: pl\n" @@ -19,42 +19,54 @@ msgstr "" "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 2.0.1\n" -#: ../app/ui.js:430 +#: ../app/ui.js:404 msgid "Connecting..." msgstr "Łączenie..." -#: ../app/ui.js:438 -msgid "Connected (encrypted) to " -msgstr "Połączenie (szyfrowane) z " - -#: ../app/ui.js:440 -msgid "Connected (unencrypted) to " -msgstr "Połączenie (nieszyfrowane) z " - -#: ../app/ui.js:446 +#: ../app/ui.js:411 msgid "Disconnecting..." msgstr "Rozłączanie..." -#: ../app/ui.js:450 -msgid "Disconnected" -msgstr "Rozłączony" - -#: ../app/ui.js:1052 ../core/rfb.js:248 -msgid "Must set host" -msgstr "Host i port są wymagane" - -#: ../app/ui.js:1101 +#: ../app/ui.js:417 msgid "Reconnecting..." msgstr "Łączenie..." -#: ../app/ui.js:1140 +#: ../app/ui.js:422 +msgid "Internal error" +msgstr "Błąd wewnętrzny" + +#: ../app/ui.js:1019 +msgid "Must set host" +msgstr "Host i port są wymagane" + +#: ../app/ui.js:1099 +msgid "Connected (encrypted) to " +msgstr "Połączenie (szyfrowane) z " + +#: ../app/ui.js:1101 +msgid "Connected (unencrypted) to " +msgstr "Połączenie (nieszyfrowane) z " + +#: ../app/ui.js:1119 +msgid "Something went wrong, connection is closed" +msgstr "Coś poszło źle, połączenie zostało zamknięte" + +#: ../app/ui.js:1129 +msgid "Disconnected" +msgstr "Rozłączony" + +#: ../app/ui.js:1142 +msgid "New connection has been rejected with reason: " +msgstr "Nowe połączenie zostało odrzucone z powodu: " + +#: ../app/ui.js:1145 +msgid "New connection has been rejected" +msgstr "Nowe połączenie zostało odrzucone" + +#: ../app/ui.js:1166 msgid "Password is required" msgstr "Hasło jest wymagane" -#: ../core/rfb.js:548 -msgid "Disconnect timeout" -msgstr "Timeout rozłączenia" - #: ../vnc.html:89 msgid "noVNC encountered an error:" msgstr "noVNC napotkało błąd:" @@ -212,77 +224,78 @@ msgid "Local Scaling" msgstr "Skalowanie lokalne" #: ../vnc.html:216 -msgid "Local Downscaling" -msgstr "Downscaling lokalny" - -#: ../vnc.html:217 msgid "Remote Resizing" msgstr "Skalowanie zdalne" -#: ../vnc.html:222 +#: ../vnc.html:221 msgid "Advanced" msgstr "Zaawansowane" -#: ../vnc.html:225 -msgid "Local Cursor" -msgstr "Lokalny kursor" - -#: ../vnc.html:229 +#: ../vnc.html:224 msgid "Repeater ID:" msgstr "ID Repeatera:" -#: ../vnc.html:233 +#: ../vnc.html:228 msgid "WebSocket" msgstr "WebSocket" -#: ../vnc.html:236 +#: ../vnc.html:231 msgid "Encrypt" msgstr "Szyfrowanie" -#: ../vnc.html:239 +#: ../vnc.html:234 msgid "Host:" msgstr "Host:" -#: ../vnc.html:243 +#: ../vnc.html:238 msgid "Port:" msgstr "Port:" -#: ../vnc.html:247 +#: ../vnc.html:242 msgid "Path:" msgstr "Ścieżka:" -#: ../vnc.html:254 +#: ../vnc.html:249 msgid "Automatic Reconnect" msgstr "Automatycznie wznawiaj połączenie" -#: ../vnc.html:257 +#: ../vnc.html:252 msgid "Reconnect Delay (ms):" msgstr "Opóźnienie wznawiania (ms):" -#: ../vnc.html:263 +#: ../vnc.html:258 msgid "Logging:" msgstr "Poziom logowania:" -#: ../vnc.html:275 +#: ../vnc.html:270 msgid "Disconnect" msgstr "Rozłącz" -#: ../vnc.html:294 +#: ../vnc.html:289 msgid "Connect" msgstr "Połącz" -#: ../vnc.html:304 +#: ../vnc.html:299 msgid "Password:" msgstr "Hasło:" -#: ../vnc.html:318 +#: ../vnc.html:313 msgid "Cancel" msgstr "Anuluj" -#: ../vnc.html:334 +#: ../vnc.html:329 msgid "Canvas not supported." msgstr "Element Canvas nie jest wspierany." +#~ msgid "Disconnect timeout" +#~ msgstr "Timeout rozłączenia" + +#~ msgid "Local Downscaling" +#~ msgstr "Downscaling lokalny" + +#~ msgid "Local Cursor" +#~ msgstr "Lokalny kursor" + #~ msgid "" #~ "Forcing clipping mode since scrollbars aren't supported by IE in " #~ "fullscreen" From 110a52c57d49feee390f9ad5e4fa21af2109e738 Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Fri, 24 Nov 2017 08:26:37 +0100 Subject: [PATCH 004/764] update german translations Signed-off-by: Dominik Csapak --- app/locale/de.json | 13 +++--- po/de.po | 112 +++++++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 55 deletions(-) diff --git a/app/locale/de.json b/app/locale/de.json index ea56e6d7..62e73360 100644 --- a/app/locale/de.json +++ b/app/locale/de.json @@ -1,13 +1,16 @@ { "Connecting...": "Verbinden...", + "Disconnecting...": "Verbindung trennen...", + "Reconnecting...": "Verbindung wiederherstellen...", + "Internal error": "Interner Fehler", + "Must set host": "Richten Sie den Server ein", "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ", "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ", - "Disconnecting...": "Verbindung trennen...", + "Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt", "Disconnected": "Verbindung zum Server getrennt", - "Must set host": "Richten Sie den Server ein", - "Reconnecting...": "Verbindung wiederherstellen...", + "New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ", + "New connection has been rejected": "Verbindung wurde abgelehnt", "Password is required": "Passwort ist erforderlich", - "Disconnect timeout": "Zeitüberschreitung beim Trennen", "noVNC encountered an error:": "Ein Fehler ist aufgetreten:", "Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen", "Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen", @@ -47,10 +50,8 @@ "Scaling Mode:": "Skalierungsmodus:", "None": "Keiner", "Local Scaling": "Lokales skalieren", - "Local Downscaling": "Lokales herunterskalieren", "Remote Resizing": "Serverseitiges skalieren", "Advanced": "Erweitert", - "Local Cursor": "Lokaler Mauszeiger", "Repeater ID:": "Repeater ID:", "WebSocket": "WebSocket", "Encrypt": "Verschlüsselt", diff --git a/po/de.po b/po/de.po index f478d9e9..ee690465 100644 --- a/po/de.po +++ b/po/de.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: noVNC 0.6.1\n" "Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-10-06 10:07+0200\n" -"PO-Revision-Date: 2017-10-10 16:41+0200\n" +"POT-Creation-Date: 2017-11-24 07:16+0000\n" +"PO-Revision-Date: 2017-11-24 08:20+0100\n" "Last-Translator: Dominik Csapak \n" "Language-Team: none\n" "Language: de\n" @@ -17,43 +17,56 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.8.11\n" -#: ../app/ui.js:430 +#: ../app/ui.js:404 msgid "Connecting..." msgstr "Verbinden..." -#: ../app/ui.js:438 -msgid "Connected (encrypted) to " -msgstr "Verbunden mit (verschlüsselt) " - -#: ../app/ui.js:440 -msgid "Connected (unencrypted) to " -msgstr "Verbunden mit (unverschlüsselt) " - -#: ../app/ui.js:446 +#: ../app/ui.js:411 msgid "Disconnecting..." msgstr "Verbindung trennen..." -#: ../app/ui.js:450 -msgid "Disconnected" -msgstr "Verbindung zum Server getrennt" - -#: ../app/ui.js:1052 ../core/rfb.js:248 -msgid "Must set host" -msgstr "Richten Sie den Server ein" - -#: ../app/ui.js:1101 +#: ../app/ui.js:417 msgid "Reconnecting..." msgstr "Verbindung wiederherstellen..." -#: ../app/ui.js:1140 +#: ../app/ui.js:422 +msgid "Internal error" +msgstr "Interner Fehler" + +#: ../app/ui.js:1019 +msgid "Must set host" +msgstr "Richten Sie den Server ein" + +#: ../app/ui.js:1099 +msgid "Connected (encrypted) to " +msgstr "Verbunden mit (verschlüsselt) " + +#: ../app/ui.js:1101 +msgid "Connected (unencrypted) to " +msgstr "Verbunden mit (unverschlüsselt) " + +#: ../app/ui.js:1119 +msgid "Something went wrong, connection is closed" +msgstr "Etwas lief schief, Verbindung wurde getrennt" + +#: ../app/ui.js:1129 +msgid "Disconnected" +msgstr "Verbindung zum Server getrennt" + +#: ../app/ui.js:1142 +msgid "New connection has been rejected with reason: " +msgstr "Verbindung wurde aus folgendem Grund abgelehnt: " + +#: ../app/ui.js:1145 +msgid "New connection has been rejected" +msgstr "Verbindung wurde abgelehnt" + +#: ../app/ui.js:1166 msgid "Password is required" msgstr "Passwort ist erforderlich" -#: ../core/rfb.js:548 -msgid "Disconnect timeout" -msgstr "Zeitüberschreitung beim Trennen" - #: ../vnc.html:89 msgid "noVNC encountered an error:" msgstr "Ein Fehler ist aufgetreten:" @@ -211,77 +224,78 @@ msgid "Local Scaling" msgstr "Lokales skalieren" #: ../vnc.html:216 -msgid "Local Downscaling" -msgstr "Lokales herunterskalieren" - -#: ../vnc.html:217 msgid "Remote Resizing" msgstr "Serverseitiges skalieren" -#: ../vnc.html:222 +#: ../vnc.html:221 msgid "Advanced" msgstr "Erweitert" -#: ../vnc.html:225 -msgid "Local Cursor" -msgstr "Lokaler Mauszeiger" - -#: ../vnc.html:229 +#: ../vnc.html:224 msgid "Repeater ID:" msgstr "Repeater ID:" -#: ../vnc.html:233 +#: ../vnc.html:228 msgid "WebSocket" msgstr "WebSocket" -#: ../vnc.html:236 +#: ../vnc.html:231 msgid "Encrypt" msgstr "Verschlüsselt" -#: ../vnc.html:239 +#: ../vnc.html:234 msgid "Host:" msgstr "Server:" -#: ../vnc.html:243 +#: ../vnc.html:238 msgid "Port:" msgstr "Port:" -#: ../vnc.html:247 +#: ../vnc.html:242 msgid "Path:" msgstr "Pfad:" -#: ../vnc.html:254 +#: ../vnc.html:249 msgid "Automatic Reconnect" msgstr "Automatisch wiederverbinden" -#: ../vnc.html:257 +#: ../vnc.html:252 msgid "Reconnect Delay (ms):" msgstr "Wiederverbindungsverzögerung (ms):" -#: ../vnc.html:263 +#: ../vnc.html:258 msgid "Logging:" msgstr "Protokollierung:" -#: ../vnc.html:275 +#: ../vnc.html:270 msgid "Disconnect" msgstr "Verbindung trennen" -#: ../vnc.html:294 +#: ../vnc.html:289 msgid "Connect" msgstr "Verbinden" -#: ../vnc.html:304 +#: ../vnc.html:299 msgid "Password:" msgstr "Passwort:" -#: ../vnc.html:318 +#: ../vnc.html:313 msgid "Cancel" msgstr "Abbrechen" -#: ../vnc.html:334 +#: ../vnc.html:329 msgid "Canvas not supported." msgstr "Canvas nicht unterstützt." +#~ msgid "Disconnect timeout" +#~ msgstr "Zeitüberschreitung beim Trennen" + +#~ msgid "Local Downscaling" +#~ msgstr "Lokales herunterskalieren" + +#~ msgid "Local Cursor" +#~ msgstr "Lokaler Mauszeiger" + #~ msgid "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen" #~ msgstr "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt" From 7f3986815879421432ba6f4ea4b16c15925daccd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 24 Nov 2017 13:10:35 +0100 Subject: [PATCH 005/764] Remove noisy websock debug logging It generates too many log lines that it drowns out everything else. --- core/websock.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/websock.js b/core/websock.js index 555076bc..03909bca 100644 --- a/core/websock.js +++ b/core/websock.js @@ -168,10 +168,6 @@ Websock.prototype = { // Send Queue flush: function () { - if (this._websocket.bufferedAmount !== 0) { - Log.Debug("bufferedAmount: " + this._websocket.bufferedAmount); - } - if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) { this._websocket.send(this._encode_message()); this._sQlen = 0; From baf822d3de32678d5d895c8c7bb1942731e64173 Mon Sep 17 00:00:00 2001 From: Josiah White Date: Thu, 30 Nov 2017 03:17:02 -0500 Subject: [PATCH 006/764] Initalize cached 16x16 tile. --- core/display.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/display.js b/core/display.js index e61802a6..91952d1f 100644 --- a/core/display.js +++ b/core/display.js @@ -69,6 +69,7 @@ export default function Display(target) { throw new Error("Canvas does not support createImageData"); } + this._tile16x16 = this._drawCtx.createImageData(16, 16); Log.Debug("<< Display.constructor"); }; From bb25d3d6c5ebd3226490cc9269b136f178d94308 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 29 Nov 2017 16:37:35 +0100 Subject: [PATCH 007/764] Forced cleanup of RFB objects in tests We need to make sure RFB objects are properly disposed or they might have event listeners and other stuff hanging around that can influence subsequent tests. --- tests/test.rfb.js | 85 +++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 81ee1dd6..542ce514 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -55,12 +55,30 @@ describe('Remote Frame Buffer Protocol Client', function() { this.clock.restore(); }); + var rfbs; + + beforeEach(function () { + // Track all created RFB objects + rfbs = []; + }); + afterEach(function () { + // Make sure every created RFB object is properly cleaned up + // or they might affect subsequent tests + rfbs.forEach(function (rfb) { + rfb.disconnect(); + expect(rfb._disconnect).to.have.been.called; + }); + rfbs = []; + }); + function make_rfb (url, options) { url = url || 'wss://host:8675'; var rfb = new RFB(document.createElement('canvas'), url, options); clock.tick(); rfb._sock._websocket._open(); rfb._rfb_connection_state = 'connected'; + sinon.spy(rfb, "_disconnect"); + rfbs.push(rfb); return rfb; } @@ -161,7 +179,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should not send the keys if we are not in a normal state', function () { sinon.spy(client._sock, 'flush'); - client._rfb_connection_state = "broken"; + client._rfb_connection_state = "connecting"; client.sendCtrlAltDel(); expect(client._sock.flush).to.not.have.been.called; }); @@ -192,7 +210,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should not send the key if we are not in a normal state', function () { sinon.spy(client._sock, 'flush'); - client._rfb_connection_state = "broken"; + client._rfb_connection_state = "connecting"; client.sendKey(123, 'Key123'); expect(client._sock.flush).to.not.have.been.called; }); @@ -231,7 +249,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should not send the text if we are not in a normal state', function () { sinon.spy(client._sock, 'flush'); - client._rfb_connection_state = "broken"; + client._rfb_connection_state = "connecting"; client.clipboardPasteFrom('abc'); expect(client._sock.flush).to.not.have.been.called; }); @@ -269,7 +287,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should not send the request if we are not in a normal state', function () { sinon.spy(client._sock, 'flush'); - client._rfb_connection_state = "broken"; + client._rfb_connection_state = "connecting"; client.requestDesktopSize(1,2); expect(client._sock.flush).to.not.have.been.called; }); @@ -321,28 +339,32 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should set the rfb_connection_state', function () { - client._rfb_connection_state = 'disconnecting'; - client._updateConnectionState('disconnected'); - expect(client._rfb_connection_state).to.equal('disconnected'); + client._rfb_connection_state = 'connecting'; + client._updateConnectionState('connected'); + expect(client._rfb_connection_state).to.equal('connected'); }); it('should not change the state when we are disconnected', function () { - client._rfb_connection_state = 'disconnected'; + client.disconnect(); + expect(client._rfb_connection_state).to.equal('disconnected'); client._updateConnectionState('connecting'); expect(client._rfb_connection_state).to.not.equal('connecting'); }); it('should ignore state changes to the same state', function () { var connectSpy = sinon.spy(); - var disconnectSpy = sinon.spy(); client.addEventListener("connect", connectSpy); - client.addEventListener("disconnect", disconnectSpy); - client._rfb_connection_state = 'connected'; + expect(client._rfb_connection_state).to.equal('connected'); client._updateConnectionState('connected'); expect(connectSpy).to.not.have.been.called; - client._rfb_connection_state = 'disconnected'; + client.disconnect(); + + var disconnectSpy = sinon.spy(); + client.addEventListener("disconnect", disconnectSpy); + + expect(client._rfb_connection_state).to.equal('disconnected'); client._updateConnectionState('disconnected'); expect(disconnectSpy).to.not.have.been.called; }); @@ -350,7 +372,6 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should ignore illegal state changes', function () { var spy = sinon.spy(); client.addEventListener("disconnect", spy); - client._rfb_connection_state = 'connected'; client._updateConnectionState('disconnected'); expect(client._rfb_connection_state).to.not.equal('disconnected'); expect(spy).to.not.have.been.called; @@ -460,11 +481,21 @@ describe('Remote Frame Buffer Protocol Client', function() { client._updateConnectionState('disconnecting'); expect(client._sock.close).to.have.been.calledOnce; }); + + it('should not result in a disconnect event', function () { + var spy = sinon.spy(); + client.addEventListener("disconnect", spy); + client._sock._websocket.close = function () {}; // explicitly don't call onclose + client._updateConnectionState('disconnecting'); + expect(spy).to.not.have.been.called; + }); }); describe('disconnected', function () { var client; - beforeEach(function () { client = make_rfb(); }); + beforeEach(function () { + client = new RFB(document.createElement('canvas'), 'ws://HOST:8675/PATH'); + }); it('should result in a disconnect event if state becomes "disconnected"', function () { var spy = sinon.spy(); @@ -475,14 +506,6 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(spy.args[0][0].detail.clean).to.be.true; }); - it('should not result in a disconnect event if the state is not "disconnected"', function () { - var spy = sinon.spy(); - client.addEventListener("disconnect", spy); - client._sock._websocket.close = function () {}; // explicitly don't call onclose - client._updateConnectionState('disconnecting'); - expect(spy).to.not.have.been.called; - }); - it('should result in a disconnect event without msg when no reason given', function () { var spy = sinon.spy(); client.addEventListener("disconnect", spy); @@ -892,7 +915,6 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should fall through to ServerInitialisation on a response code of 0', function () { - client._updateConnectionState = sinon.spy(); client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0])); expect(client._rfb_init_state).to.equal('ServerInitialisation'); }); @@ -1898,14 +1920,18 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should fail if we are not currently ready to connect and we get an "open" event', function () { sinon.spy(client, "_fail"); - client._rfb_connection_state = 'some_other_state'; + client._rfb_connection_state = 'connected'; client._sock._websocket._open(); expect(client._fail).to.have.been.calledOnce; }); // close events it('should transition to "disconnected" from "disconnecting" on a close event', function () { - client._rfb_connection_state = 'disconnecting'; + var real = client._sock._websocket.close; + client._sock._websocket.close = function () {}; + client.disconnect(); + expect(client._rfb_connection_state).to.equal('disconnecting'); + client._sock._websocket.close = real; client._sock._websocket.close(); expect(client._rfb_connection_state).to.equal('disconnected'); }); @@ -1917,16 +1943,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._fail).to.have.been.calledOnce; }); - it('should fail if we get a close event while disconnected', function () { - sinon.spy(client, "_fail"); - client._rfb_connection_state = 'disconnected'; - client._sock._websocket.close(); - expect(client._fail).to.have.been.calledOnce; - }); - it('should unregister close event handler', function () { sinon.spy(client._sock, 'off'); - client._rfb_connection_state = 'disconnecting'; + client.disconnect(); client._sock._websocket.close(); expect(client._sock.off).to.have.been.calledWith('close'); }); From 898cd32c0717d491e116c3cebfac6e656f703d41 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 30 Nov 2017 16:11:23 +0100 Subject: [PATCH 008/764] Don't send pointer event on end of drag We should only send an event to the server if we didn't actually end up dragging the viewport. --- core/rfb.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index 41157452..a5bd843e 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -634,18 +634,26 @@ RFB.prototype = { if (down && !this._viewportDragging) { this._viewportDragging = true; this._viewportDragPos = {'x': x, 'y': y}; + this._viewportHasMoved = false; // Skip sending mouse events return; } else { this._viewportDragging = false; - // If the viewport didn't actually move, then treat as a mouse click event - // Send the button down event here, as the button up event is sent at the end of this function - if (!this._viewportHasMoved && !this._viewOnly) { - RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), bmask); + // If we actually performed a drag then we are done + // here and should not send any mouse events + if (this._viewportHasMoved) { + return; } - this._viewportHasMoved = false; + + // Otherwise we treat this as a mouse click event. + // Send the button down event here, as the button up + // event is sent at the end of this function. + RFB.messages.pointerEvent(this._sock, + this._display.absX(x), + this._display.absY(y), + bmask); } } From 9b84f51685243309afb9ca4cefe1de697594705b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 24 Nov 2017 15:25:23 +0100 Subject: [PATCH 009/764] Move resize handling in to RFB object Makes the API simpler and makes it easier for other frontends to get this functionality. --- app/styles/base.css | 24 -- app/styles/lite.css | 7 - app/ui.js | 150 +---------- core/display.js | 5 - core/rfb.js | 240 ++++++++++++----- docs/API-internal.md | 1 - docs/API.md | 140 ++++------ tests/test.display.js | 9 - tests/test.rfb.js | 589 +++++++++++++++++++++++++++++++----------- vnc.html | 23 +- vnc_lite.html | 36 +-- 11 files changed, 691 insertions(+), 533 deletions(-) diff --git a/app/styles/base.css b/app/styles/base.css index b8ce81bd..344db9b2 100644 --- a/app/styles/base.css +++ b/app/styles/base.css @@ -854,30 +854,6 @@ select:active { ime-mode: disabled; } -/* HTML5 Canvas */ -#noVNC_screen { - display: flex; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgb(40, 40, 40); -} -:root:not(.noVNC_connected) #noVNC_screen { - display: none; -} - -/* Do not set width/height for VNC_canvas or incorrect - * scaling will occur. Canvas size depends on remote VNC - * settings and noVNC settings. */ -#noVNC_canvas { - margin: auto; - /* IE miscalculates width without this :( */ - flex-shrink: 0; -} -#noVNC_canvas:focus { - outline: none; -} - /*Default noVNC logo.*/ /* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ @font-face { diff --git a/app/styles/lite.css b/app/styles/lite.css index b7df1e39..13e11c7e 100644 --- a/app/styles/lite.css +++ b/app/styles/lite.css @@ -61,10 +61,3 @@ html { display: flex; justify-content: flex-end; } - -/* Do not set width/height for VNC_canvas or incorrect - * scaling will occur. Canvas size depends on remote VNC - * settings and noVNC settings. */ -#noVNC_canvas { - margin: auto; -} diff --git a/app/ui.js b/app/ui.js index d9da9a88..3c909cd2 100644 --- a/app/ui.js +++ b/app/ui.js @@ -27,7 +27,6 @@ var UI = { connected: false, desktopName: "", - resizeTimeout: null, statusTimeout: null, hideKeyboardTimeout: null, idleControlbarTimeout: null, @@ -87,7 +86,6 @@ var UI = { UI.initFullscreen(); // Setup event handlers - UI.addResizeHandlers(); UI.addControlbarHandlers(); UI.addTouchSpecificHandlers(); UI.addExtraKeysHandlers(); @@ -103,8 +101,6 @@ var UI = { UI.openControlbar(); - UI.updateViewClip(); - UI.updateVisualState('init'); document.documentElement.classList.remove("noVNC_loading"); @@ -205,11 +201,6 @@ var UI = { * EVENT HANDLERS * ------v------*/ - addResizeHandlers: function() { - window.addEventListener('resize', UI.applyResizeMode); - window.addEventListener('resize', UI.updateViewClip); - }, - addControlbarHandlers: function() { document.getElementById("noVNC_control_bar") .addEventListener('mousemove', UI.activateControlbar); @@ -432,7 +423,6 @@ var UI = { UI.disableSetting('port'); UI.disableSetting('path'); UI.disableSetting('repeaterID'); - UI.updateViewClip(); UI.setMouseButton(1); // Hide the controlbar after 2 seconds @@ -1037,7 +1027,7 @@ var UI = { } url += '/' + path; - UI.rfb = new RFB(document.getElementById('noVNC_canvas'), url, + UI.rfb = new RFB(document.getElementById('noVNC_container'), url, { shared: UI.getSetting('shared'), repeaterID: UI.getSetting('repeaterID'), credentials: { password: password } }); @@ -1045,11 +1035,13 @@ var UI = { UI.rfb.addEventListener("disconnect", UI.disconnectFinished); UI.rfb.addEventListener("credentialsrequired", UI.credentials); UI.rfb.addEventListener("securityfailure", UI.securityFailed); - UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); UI.initialResize(); }); + UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); }); UI.rfb.addEventListener("clipboard", UI.clipboardReceive); UI.rfb.addEventListener("bell", UI.bell); - UI.rfb.addEventListener("fbresize", UI.updateSessionSize); UI.rfb.addEventListener("desktopname", UI.updateDesktopName); + UI.rfb.clipViewport = UI.getSetting('view_clip'); + UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; + UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; }, disconnect: function() { @@ -1092,7 +1084,6 @@ var UI = { connectFinished: function (e) { UI.connected = true; UI.inhibit_reconnect = false; - UI.doneInitialResize = false; let msg; if (UI.getSetting('encrypt')) { @@ -1104,7 +1095,7 @@ var UI = { UI.updateVisualState('connected'); // Do this last because it can only be used on rendered elements - document.getElementById('noVNC_canvas').focus(); + UI.rfb.focus(); }, disconnectFinished: function (e) { @@ -1238,74 +1229,8 @@ var UI = { applyResizeMode: function() { if (!UI.rfb) return; - var screen = UI.screenSize(); - - if (screen && UI.connected) { - - var resizeMode = UI.getSetting('resize'); - UI.rfb.viewportScale = 1.0; - - // Make sure the viewport is adjusted first - UI.updateViewClip(); - - if (resizeMode === 'remote') { - - // Request changing the resolution of the remote display to - // the size of the local browser viewport. - - // In order to not send multiple requests before the browser-resize - // is finished we wait 0.5 seconds before sending the request. - clearTimeout(UI.resizeTimeout); - UI.resizeTimeout = setTimeout(function(){ - // Request a remote size covering the viewport - if (UI.rfb.requestDesktopSize(screen.w, screen.h)) { - Log.Debug('Requested new desktop size: ' + - screen.w + 'x' + screen.h); - } - }, 500); - - } else { - UI.updateScaling(); - } - } - }, - - // Re-calculate local scaling - updateScaling: function() { - if (!UI.rfb) return; - - var resizeMode = UI.getSetting('resize'); - if (resizeMode !== 'scale') { - return; - } - - var screen = UI.screenSize(); - - if (!screen || !UI.connected) { - return; - } - - UI.rfb.autoscale(screen.w, screen.h); - UI.fixScrollbars(); - }, - - // Gets the the size of the available viewport in the browser window - screenSize: function() { - var screen = document.getElementById('noVNC_screen'); - return {w: screen.offsetWidth, h: screen.offsetHeight}; - }, - - // Normally we only apply the current resize mode after a window resize - // event. This means that when a new connection is opened, there is no - // resize mode active. - // We have to wait until we know the capabilities of the server as - // some calls later in the chain is dependant on knowing the - // server-capabilities. - initialResize: function() { - if (UI.doneInitialResize) return; - - UI.applyResizeMode(); - UI.doneInitialResize = true; + UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; + UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; }, /* ------^------- @@ -1314,12 +1239,6 @@ var UI = { * VIEW CLIPPING * ------v------*/ - // Set and configure viewport clipping - setViewClip: function(clip) { - UI.updateSetting('view_clip', clip); - UI.updateViewClip(); - }, - // Update parameters that depend on the viewport clip setting updateViewClip: function() { if (!UI.rfb) return; @@ -1327,11 +1246,7 @@ var UI = { var cur_clip = UI.rfb.clipViewport; var new_clip = UI.getSetting('view_clip'); - var resizeSetting = UI.getSetting('resize'); - if (resizeSetting === 'scale') { - // Disable viewport clipping if we are scaling - new_clip = false; - } else if (isTouchDevice) { + if (isTouchDevice) { // Touch devices usually have shit scrollbars new_clip = true; } @@ -1340,15 +1255,6 @@ var UI = { UI.rfb.clipViewport = new_clip; } - var size = UI.screenSize(); - - if (new_clip && size) { - // When clipping is enabled, the screen is limited to - // the size of the browser window. - UI.rfb.viewportChangeSize(size.w, size.h); - UI.fixScrollbars(); - } - // Changing the viewport may change the state of // the dragging button UI.updateViewDrag(); @@ -1389,23 +1295,13 @@ var UI = { }, updateViewDrag: function() { - var clipping = false; - if (!UI.connected) return; - // Check if viewport drag is possible. It is only possible - // if the remote display is clipping the client display. - if (UI.rfb.clipViewport && UI.rfb.isClipped) { - clipping = true; - } - var viewDragButton = document.getElementById('noVNC_view_drag_button'); - if (!clipping && - UI.rfb.dragViewport) { - // The size of the remote display is the same or smaller - // than the client display. Make sure viewport drag isn't - // active when it can't be used. + if (!UI.rfb.clipViewport && UI.rfb.dragViewport) { + // We are no longer clipping the viewport. Make sure + // viewport drag isn't active when it can't be used. UI.rfb.dragViewport = false; } @@ -1420,7 +1316,7 @@ var UI = { if (isTouchDevice) { viewDragButton.classList.remove("noVNC_hidden"); - if (clipping) { + if (UI.rfb.clipViewport) { viewDragButton.disabled = false; } else { viewDragButton.disabled = true; @@ -1428,7 +1324,7 @@ var UI = { } else { viewDragButton.disabled = false; - if (clipping) { + if (UI.rfb.clipViewport) { viewDragButton.classList.remove("noVNC_hidden"); } else { viewDragButton.classList.add("noVNC_hidden"); @@ -1703,24 +1599,6 @@ var UI = { WebUtil.init_logging(UI.getSetting('logging')); }, - updateSessionSize: function(e) { - UI.updateViewClip(); - UI.updateScaling(); - UI.fixScrollbars(); - }, - - fixScrollbars: function() { - // This is a hack because Chrome screws up the calculation - // for when scrollbars are needed. So to fix it we temporarily - // toggle them off and on. - var screen = document.getElementById('noVNC_screen'); - screen.style.overflow = 'hidden'; - // Force Chrome to recalculate the layout by asking for - // an element's dimensions - screen.getBoundingClientRect(); - screen.style.overflow = ""; - }, - updateDesktopName: function(e) { UI.desktopName = e.detail.name; // Display the desktop name in the document title diff --git a/core/display.js b/core/display.js index e61802a6..b252f99e 100644 --- a/core/display.js +++ b/core/display.js @@ -106,11 +106,6 @@ Display.prototype = { return this._fb_height; }, - get isClipped() { - var vp = this._viewportLoc; - return this._fb_width > vp.w || this._fb_height > vp.h; - }, - logo: null, // ===== EVENT HANDLERS ===== diff --git a/core/rfb.js b/core/rfb.js index a5bd843e..63c6c6ba 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -66,7 +66,7 @@ export default function RFB(target, url, options) { this._fb_name = ""; - this._capabilities = { power: false, resize: false }; + this._capabilities = { power: false }; this._supportsFence = false; @@ -88,6 +88,7 @@ export default function RFB(target, url, options) { // Timers this._disconnTimer = null; // disconnection timer + this._resizeTimeout = null; // resize rate limiting // Decoder states and stats this._encHandlers = {}; @@ -140,15 +141,29 @@ export default function RFB(target, url, options) { // Bound event handlers this._eventHandlers = { focusCanvas: this._focusCanvas.bind(this), + windowResize: this._windowResize.bind(this), }; // main setup Log.Debug(">> RFB.constructor"); - // Target canvas must be able to have focus - if (!this._target.hasAttribute('tabindex')) { - this._target.tabIndex = -1; - } + // Create DOM elements + this._screen = document.createElement('div'); + this._screen.style.display = 'flex'; + this._screen.style.width = '100%'; + this._screen.style.height = '100%'; + this._screen.style.overflow = 'auto'; + this._screen.style.backgroundColor = 'rgb(40, 40, 40)'; + this._canvas = document.createElement('canvas'); + this._canvas.style.margin = 'auto'; + // Some browsers add an outline on focus + this._canvas.style.outline = 'none'; + // IE miscalculates width without this :( + this._canvas.style.flexShrink = '0'; + this._canvas.width = 0; + this._canvas.height = 0; + this._canvas.tabIndex = -1; + this._screen.appendChild(this._canvas); // populate encHandlers with bound versions this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this); @@ -166,7 +181,7 @@ export default function RFB(target, url, options) { // NB: nothing that needs explicit teardown should be done // before this point, since this can throw an exception try { - this._display = new Display(this._target); + this._display = new Display(this._canvas); } catch (exc) { Log.Error("Display exception: " + exc); throw exc; @@ -174,10 +189,10 @@ export default function RFB(target, url, options) { this._display.onflush = this._onFlush.bind(this); this._display.clear(); - this._keyboard = new Keyboard(this._target); + this._keyboard = new Keyboard(this._canvas); this._keyboard.onkeyevent = this._handleKeyEvent.bind(this); - this._mouse = new Mouse(this._target); + this._mouse = new Mouse(this._canvas); this._mouse.onmousebutton = this._handleMouseButton.bind(this); this._mouse.onmousemove = this._handleMouseMove.bind(this); @@ -266,13 +281,36 @@ RFB.prototype = { get touchButton() { return this._mouse.touchButton; }, set touchButton(button) { this._mouse.touchButton = button; }, - get viewportScale() { return this._display.scale; }, - set viewportScale(scale) { this._display.scale = scale; }, + _clipViewport: false, + get clipViewport() { return this._clipViewport; }, + set clipViewport(viewport) { + this._clipViewport = viewport; + this._updateClip(); + }, - get clipViewport() { return this._display.clipViewport; }, - set clipViewport(viewport) { this._display.clipViewport = viewport; }, + _scaleViewport: false, + get scaleViewport() { return this._scaleViewport; }, + set scaleViewport(scale) { + this._scaleViewport = scale; + // Scaling trumps clipping, so we may need to adjust + // clipping when enabling or disabling scaling + if (scale && this._clipViewport) { + this._updateClip(); + } + this._updateScale(); + if (!scale && this._clipViewport) { + this._updateClip(); + } + }, - get isClipped() { return this._display.isClipped; }, + _resizeSession: false, + get resizeSession() { return this._resizeSession; }, + set resizeSession(resize) { + this._resizeSession = resize; + if (resize) { + this._requestRemoteResize(); + } + }, // ===== PUBLIC METHODS ===== @@ -341,38 +379,19 @@ RFB.prototype = { } }, + focus: function () { + this._canvas.focus(); + }, + + blur: function () { + this._canvas.blur(); + }, + clipboardPasteFrom: function (text) { if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; } RFB.messages.clientCutText(this._sock, text); }, - autoscale: function (width, height) { - if (this._rfb_connection_state !== 'connected') { return; } - this._display.autoscale(width, height); - }, - - viewportChangeSize: function(width, height) { - if (this._rfb_connection_state !== 'connected') { return; } - this._display.viewportChangeSize(width, height); - }, - - // Requests a change of remote desktop size. This message is an extension - // and may only be sent if we have received an ExtendedDesktopSize message - requestDesktopSize: function (width, height) { - if (this._rfb_connection_state !== 'connected' || - this._viewOnly) { - return; - } - - if (!this._supportsSetDesktopSize) { - return; - } - - RFB.messages.setDesktopSize(this._sock, width, height, - this._screen_id, this._screen_flags); - }, - - // ===== PRIVATE METHODS ===== _connect: function () { @@ -391,20 +410,31 @@ RFB.prototype = { } } + // Make our elements part of the page + this._target.appendChild(this._screen); + + // Monitor size changes of the screen + // FIXME: Use ResizeObserver, or hidden overflow + window.addEventListener('resize', this._eventHandlers.windowResize); + // Always grab focus on some kind of click event - this._target.addEventListener("mousedown", this._eventHandlers.focusCanvas); - this._target.addEventListener("touchstart", this._eventHandlers.focusCanvas); + this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas); + this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas); Log.Debug("<< RFB.connect"); }, _disconnect: function () { Log.Debug(">> RFB.disconnect"); - this._target.removeEventListener("mousedown", this._eventHandlers.focusCanvas); - this._target.removeEventListener("touchstart", this._eventHandlers.focusCanvas); - this._cleanup(); + this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas); + this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas); + window.removeEventListener('resize', this._eventHandlers.windowResize); + this._keyboard.ungrab(); + this._mouse.ungrab(); this._sock.close(); this._print_stats(); + this._target.removeChild(this._screen); + clearTimeout(this._resizeTimeout); Log.Debug("<< RFB.disconnect"); }, @@ -426,17 +456,6 @@ RFB.prototype = { }); }, - _cleanup: function () { - if (!this._viewOnly) { this._keyboard.ungrab(); } - if (!this._viewOnly) { this._mouse.ungrab(); } - this._display.defaultCursor(); - if (Log.get_logging() !== 'debug') { - // Show noVNC logo when disconnected, unless in - // debug mode - this._display.clear(); - } - }, - _focusCanvas: function(event) { // Respect earlier handlers' request to not do side-effects if (event.defaultPrevented) { @@ -447,7 +466,97 @@ RFB.prototype = { return; } - this._target.focus(); + this.focus(); + }, + + _windowResize: function (event) { + // If the window resized then our screen element might have + // as well. Update the viewport dimensions. + window.requestAnimationFrame(function () { + this._updateClip(); + this._updateScale(); + }.bind(this)); + + if (this._resizeSession) { + // Request changing the resolution of the remote display to + // the size of the local browser viewport. + + // In order to not send multiple requests before the browser-resize + // is finished we wait 0.5 seconds before sending the request. + clearTimeout(this._resizeTimeout); + this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500); + } + }, + + // Update state of clipping in Display object, and make sure the + // configured viewport matches the current screen size + _updateClip: function () { + var cur_clip = this._display.clipViewport; + var new_clip = this._clipViewport; + + if (this._scaleViewport) { + // Disable viewport clipping if we are scaling + new_clip = false; + } + + if (cur_clip !== new_clip) { + this._display.clipViewport = new_clip; + } + + if (new_clip) { + // When clipping is enabled, the screen is limited to + // the size of the container. + let size = this._screenSize(); + this._display.viewportChangeSize(size.w, size.h); + this._fixScrollbars(); + } + }, + + _updateScale: function () { + if (!this._scaleViewport) { + this._display.scale = 1.0; + } else { + let size = this._screenSize(); + this._display.autoscale(size.w, size.h); + } + this._fixScrollbars(); + }, + + // Requests a change of remote desktop size. This message is an extension + // and may only be sent if we have received an ExtendedDesktopSize message + _requestRemoteResize: function () { + clearTimeout(this._resizeTimeout); + this._resizeTimeout = null; + + if (!this._resizeSession || this._viewOnly || + !this._supportsSetDesktopSize) { + return; + } + + let size = this._screenSize(); + RFB.messages.setDesktopSize(this._sock, size.w, size.h, + this._screen_id, this._screen_flags); + + Log.Debug('Requested new desktop size: ' + + size.w + 'x' + size.h); + }, + + // Gets the the size of the available screen + _screenSize: function () { + return { w: this._screen.offsetWidth, + h: this._screen.offsetHeight }; + }, + + _fixScrollbars: function () { + // This is a hack because Chrome screws up the calculation + // for when scrollbars are needed. So to fix it we temporarily + // toggle them off and on. + var orig = this._screen.style.overflow; + this._screen.style.overflow = 'hidden'; + // Force Chrome to recalculate the layout by asking for + // an element's dimensions + this._screen.getBoundingClientRect(); + this._screen.style.overflow = orig; }, /* @@ -1467,10 +1576,9 @@ RFB.prototype = { this._display.resize(this._fb_width, this._fb_height); - var event = new CustomEvent("fbresize", - { detail: { width: this._fb_width, - height: this._fb_height } }); - this.dispatchEvent(event); + // Adjust the visible viewport based on the new dimensions + this._updateClip(); + this._updateScale(); this._timing.fbu_rt_start = (new Date()).getTime(); this._updateContinuousUpdates(); @@ -2308,8 +2416,16 @@ RFB.encodingHandlers = { this._FBU.bytes = 1; if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; } + var firstUpdate = !this._supportsSetDesktopSize; this._supportsSetDesktopSize = true; - this._setCapability("resize", true); + + // Normally we only apply the current resize mode after a + // window resize event. However there is no such trigger on the + // initial connect. And we don't know if the server supports + // resizing until we've gotten here. + if (firstUpdate) { + this._requestRemoteResize(); + } var number_of_screens = this._sock.rQpeek8(); diff --git a/docs/API-internal.md b/docs/API-internal.md index f030dc38..4943c1a1 100644 --- a/docs/API-internal.md +++ b/docs/API-internal.md @@ -89,7 +89,6 @@ None | clipViewport | bool | RW | false | Use viewport clipping | width | int | RO | | Display area width | height | int | RO | | Display area height -| isClipped | bool | RO | | Is the remote display is larger than the client display ### 2.3.2 Methods diff --git a/docs/API.md b/docs/API.md index 4d4f4d51..c5923e3f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -23,8 +23,8 @@ protocol stream. `focusOnClick` - Is a `boolean` indicating if keyboard focus should automatically be - moved to the canvas when a `mousedown` or `touchstart` event is - received. + moved to the remote session when a `mousedown` or `touchstart` + event is received. `touchButton` - Is a `long` controlling the button mask that should be simulated @@ -32,24 +32,26 @@ protocol stream. [`MouseEvent.button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button). Is set to `1` by default. -`viewportScale` - - Is a `double` indicating how the framebuffer contents should be - scaled before being rendered on to the canvas. See also - [`RFB.autoscale()`](#rfbautoscale). Is set to `1.0` by default. - `clipViewport` - - Is a `boolean` indicating if the canvas should be clipped to its - container. When disabled the container must be able to handle the - resulting overflow. Disabled by default. + - Is a `boolean` indicating if the remote session should be clipped + to its container. When disabled scrollbars will be shown to handle + the resulting overflow. Disabled by default. `dragViewport` - Is a `boolean` indicating if mouse events should control the - relative position of a clipped canvas. Only relevant if + relative position of a clipped remote session. Only relevant if `clipViewport` is enabled. Disabled by default. -`isClipped` *Read only* - - Is a `boolean` indicating if the framebuffer is larger than the - current canvas, i.e. it is being clipped. +`scaleViewport` + - Is a `boolean` indicating if the remote session should be scaled + locally so it fits its container. When disabled it will be centered + if the remote session is smaller than its container, or handled + according to `clipViewport` if it is larger. Disabled by default. + +`resizeSession` + - Is a `boolean` indicating if a request to resize the remote session + should be sent whenever the container changes dimensions. Disabled + by default. `capabilities` *Read only* - Is an `Object` indicating which optional extensions are available @@ -59,7 +61,6 @@ protocol stream. | name | type | description | -------- | --------- | ----------- | `power` | `boolean` | Machine power control is available - | `resize` | `boolean` | The framebuffer can be resized ### Events @@ -86,9 +87,6 @@ protocol stream. - The `bell` event is fired when a audible bell request is received from the server. -[`fbresize`](#fbresize) - - The `fbresize` event is fired when the framebuffer size is changed. - [`desktopname`](#desktopname) - The `desktopname` event is fired when the remote desktop name changes. @@ -112,6 +110,12 @@ protocol stream. [`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel) - Send Ctrl-Alt-Del key sequence. +[`RFB.focus()`](#rfbfocus) + - Move keyboard focus to the remote session. + +[`RFB.blur()`](#rfbblur) + - Remove keyboard focus from the remote session. + [`RFB.machineShutdown()`](#rfbmachineshutdown) - Request a shutdown of the remote machine. @@ -124,16 +128,6 @@ protocol stream. [`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom) - Send clipboard contents to server. -[`RFB.autoscale()`](#rfbautoscale) - - Set `RFB.viewportScale` so that the framebuffer fits a specified - container. - -[`RFB.requestDesktopSize()`](#rfbrequestDesktopSize) - - Send a request to change the remote desktop size. - -[`RFB.viewportChangeSize()`](#rfbviewportChangeSize) - - Change size of the viewport. - ### Details #### RFB() @@ -148,9 +142,10 @@ connection to a specified VNC server. ###### Parameters **`target`** - - A [`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) - that specifies where graphics should be rendered and input events - should be monitored. + - A block [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) + that specifies where the `RFB` object should attach itself. The + existing contents of the `HTMLElement` will be untouched, but new + elements will be added during the lifetime of the `RFB` object. **`url`** - A `DOMString` specifying the VNC server to connect to. This must be @@ -233,12 +228,6 @@ which is a `DOMString` with the clipboard data. The `bell` event is fired when the server has requested an audible bell. -#### fbresize - -The `fbresize` event is fired when the framebuffer has changed -dimensions. The `detail` property is an `Object` with the properties -`width` and `height` specifying the new dimensions. - #### desktopname The `desktopname` event is fired when the name of the remote desktop @@ -310,6 +299,25 @@ around [`RFB.sendKey()`](#rfbsendkey). RFB.sendCtrlAltDel( ); +#### RFB.focus() + +The `RFB.focus()` method sets the keyboard focus on the remote session. +Keyboard events will be sent to the remote server after this point. + +##### Syntax + + RFB.focus( ); + +#### RFB.blur() + +The `RFB.blur()` method remove keyboard focus on the remote session. +Keyboard events will no longer be sent to the remote server after this +point. + +##### Syntax + + RFB.blur( ); + #### RFB.machineShutdown() The `RFB.machineShutdown()` method is used to request to shut down the @@ -354,61 +362,3 @@ to the remote server. **`text`** - A `DOMString` specifying the clipboard data to send. Currently only characters from ISO 8859-1 are supported. - -#### RFB.autoscale() - -The `RFB.autoscale()` method is used to automatically adjust -`RFB.viewportScale` to fit given dimensions. - -##### Syntax - - RFB.autoscale( width, height ); - -###### Parameters - -**`width`** - - A `long` specifying the maximum width of the canvas in CSS pixels. - -**`height`** - - A `long` specifying the maximum height of the canvas in CSS pixels. - -#### RFB.requestDesktopSize() - -The `RFB.requestDesktopSize()` method is used to request a change of -the framebuffer. The capability `resize` must be set for this method to -have any effect. - -Note that this is merely a request and the server may deny it. -The [`fbresize`](#fbresize) event will be fired when the framebuffer -actually changes dimensions. - -##### Syntax - - RFB.requestDesktopSize( width, height ); - -###### Parameters - -**`width`** - - A `long` specifying the new requested width in CSS pixels. - -**`height`** - - A `long` specifying the new requested height in CSS pixels. - -#### RFB.viewportChangeSize() - -The `RFB.viewportChangeSize()` method is used to change the size of the -canvas rather than the underlying framebuffer. - -This method has no effect if `RFB.clipViewport` is set to `false`. - -##### Syntax - - RFB.viewportChangeSize( width, height ); - -###### Parameters - -**`width`** - - A `long` specifying the new width in CSS pixels. - -**`height`** - - A `long` specifying the new height in CSS pixels. diff --git a/tests/test.display.js b/tests/test.display.js index b8e9b51f..9e6f0491 100644 --- a/tests/test.display.js +++ b/tests/test.display.js @@ -91,15 +91,6 @@ describe('Display/Canvas Helper', function () { expect(display.flip).to.have.been.calledOnce; }); - it('should report clipping when framebuffer > viewport', function () { - expect(display.isClipped).to.be.true; - }); - - it('should report not clipping when framebuffer = viewport', function () { - display.viewportChangeSize(5, 5); - expect(display.isClipped).to.be.false; - }); - it('should show the entire framebuffer when disabling the viewport', function() { display.clipViewport = false; expect(display.absX(0)).to.equal(0); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 542ce514..31a7f2d5 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -9,6 +9,22 @@ import { encodings } from '../core/encodings.js'; import FakeWebSocket from './fake.websocket.js'; import sinon from '../vendor/sinon.js'; +/* UIEvent constructor polyfill for IE */ +(function () { + if (typeof window.UIEvent === "function") return; + + function UIEvent ( event, params ) { + params = params || { bubbles: false, cancelable: false, view: window, detail: undefined }; + var evt = document.createEvent( 'UIEvent' ); + evt.initUIEvent( event, params.bubbles, params.cancelable, params.view, params.detail ); + return evt; + } + + UIEvent.prototype = window.UIEvent.prototype; + + window.UIEvent = UIEvent; +})(); + var push8 = function (arr, num) { "use strict"; arr.push(num & 0xFF); @@ -30,12 +46,16 @@ var push32 = function (arr, num) { describe('Remote Frame Buffer Protocol Client', function() { var clock; + var raf; before(FakeWebSocket.replace); after(FakeWebSocket.restore); before(function () { this.clock = clock = sinon.useFakeTimers(); + // sinon doesn't support this yet + raf = window.requestAnimationFrame; + window.requestAnimationFrame = setTimeout; // Use a single set of buffers instead of reallocating to // speed up tests var sock = new Websock(); @@ -53,12 +73,20 @@ describe('Remote Frame Buffer Protocol Client', function() { after(function () { Websock.prototype._allocate_buffers = Websock.prototype._old_allocate_buffers; this.clock.restore(); + window.requestAnimationFrame = raf; }); + var container; var rfbs; beforeEach(function () { - // Track all created RFB objects + // Create a container element for all RFB objects to attach to + container = document.createElement('div'); + container.style.width = "100%"; + container.style.height = "100%"; + document.body.appendChild(container); + + // And track all created RFB objects rfbs = []; }); afterEach(function () { @@ -69,11 +97,14 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(rfb._disconnect).to.have.been.called; }); rfbs = []; + + document.body.removeChild(container); + container = null; }); function make_rfb (url, options) { url = url || 'wss://host:8675'; - var rfb = new RFB(document.createElement('canvas'), url, options); + var rfb = new RFB(container, url, options); clock.tick(); rfb._sock._websocket._open(); rfb._rfb_connection_state = 'connected'; @@ -85,14 +116,14 @@ describe('Remote Frame Buffer Protocol Client', function() { describe('Connecting/Disconnecting', function () { describe('#RFB', function () { it('should set the current state to "connecting"', function () { - var client = new RFB(document.createElement('canvas'), 'wss://host:8675'); + var client = new RFB(document.createElement('div'), 'wss://host:8675'); client._rfb_connection_state = ''; this.clock.tick(); expect(client._rfb_connection_state).to.equal('connecting'); }); it('should actually connect to the websocket', function () { - var client = new RFB(document.createElement('canvas'), 'ws://HOST:8675/PATH'); + var client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH'); sinon.spy(client._sock, 'open'); this.clock.tick(); expect(client._sock.open).to.have.been.calledOnce; @@ -239,6 +270,22 @@ describe('Remote Frame Buffer Protocol Client', function() { }); }); + describe('#focus', function () { + it('should move focus to canvas object', function () { + client._canvas.focus = sinon.spy(); + client.focus(); + expect(client._canvas.focus).to.have.been.called.once; + }); + }); + + describe('#blur', function () { + it('should remove focus from canvas object', function () { + client._canvas.blur = sinon.spy(); + client.blur(); + expect(client._canvas.blur).to.have.been.called.once; + }); + }); + describe('#clipboardPasteFrom', function () { it('should send the given text in a paste event', function () { var expected = {_sQ: new Uint8Array(11), _sQlen: 0, flush: function () {}}; @@ -255,44 +302,6 @@ describe('Remote Frame Buffer Protocol Client', function() { }); }); - describe("#requestDesktopSize", function () { - beforeEach(function() { - client._supportsSetDesktopSize = true; - }); - - it('should send the request with the given width and height', function () { - var expected = [251]; - push8(expected, 0); // padding - push16(expected, 1); // width - push16(expected, 2); // height - push8(expected, 1); // number-of-screens - push8(expected, 0); // padding before screen array - push32(expected, 0); // id - push16(expected, 0); // x-position - push16(expected, 0); // y-position - push16(expected, 1); // width - push16(expected, 2); // height - push32(expected, 0); // flags - - client.requestDesktopSize(1, 2); - expect(client._sock).to.have.sent(new Uint8Array(expected)); - }); - - it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () { - sinon.spy(client._sock, 'flush'); - client._supportsSetDesktopSize = false; - client.requestDesktopSize(1,2); - expect(client._sock.flush).to.not.have.been.called; - }); - - it('should not send the request if we are not in a normal state', function () { - sinon.spy(client._sock, 'flush'); - client._rfb_connection_state = "connecting"; - client.requestDesktopSize(1,2); - expect(client._sock.flush).to.not.have.been.called; - }); - }); - describe("XVP operations", function () { beforeEach(function () { client._rfb_xvp_ver = 1; @@ -321,6 +330,394 @@ describe('Remote Frame Buffer Protocol Client', function() { }); }); + describe('Clipping', function () { + var client; + beforeEach(function () { + client = make_rfb(); + container.style.width = '70px'; + container.style.height = '80px'; + client.clipViewport = true; + }); + + it('should update display clip state when changing the property', function () { + var spy = sinon.spy(client._display, "clipViewport", ["set"]); + + client.clipViewport = false; + expect(spy.set).to.have.been.calledOnce; + expect(spy.set).to.have.been.calledWith(false); + spy.set.reset(); + + client.clipViewport = true; + expect(spy.set).to.have.been.calledOnce; + expect(spy.set).to.have.been.calledWith(true); + }); + + it('should update the viewport when the container size changes', function () { + sinon.spy(client._display, "viewportChangeSize"); + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(); + + expect(client._display.viewportChangeSize).to.have.been.calledOnce; + expect(client._display.viewportChangeSize).to.have.been.calledWith(40, 50); + }); + + it('should update the viewport when the remote session resizes', function () { + // Simple ExtendedDesktopSize FBU message + var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00 ]; + + sinon.spy(client._display, "viewportChangeSize"); + + client._sock._websocket._receive_data(new Uint8Array(incoming)); + + // FIXME: Display implicitly calls viewportChangeSize() when + // resizing the framebuffer, hence calledTwice. + expect(client._display.viewportChangeSize).to.have.been.calledTwice; + expect(client._display.viewportChangeSize).to.have.been.calledWith(70, 80); + }); + + it('should not update the viewport if not clipping', function () { + client.clipViewport = false; + sinon.spy(client._display, "viewportChangeSize"); + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(); + + expect(client._display.viewportChangeSize).to.not.have.been.called; + }); + + it('should not update the viewport if scaling', function () { + client.scaleViewport = true; + sinon.spy(client._display, "viewportChangeSize"); + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(); + + expect(client._display.viewportChangeSize).to.not.have.been.called; + }); + + describe('Dragging', function () { + beforeEach(function () { + client.dragViewport = true; + sinon.spy(RFB.messages, "pointerEvent"); + }); + + afterEach(function () { + RFB.messages.pointerEvent.restore(); + }); + + it('should not send button messages when initiating viewport dragging', function () { + client._handleMouseButton(13, 9, 0x001); + expect(RFB.messages.pointerEvent).to.not.have.been.called; + }); + + it('should send button messages when release without movement', function () { + // Just up and down + client._handleMouseButton(13, 9, 0x001); + client._handleMouseButton(13, 9, 0x000); + expect(RFB.messages.pointerEvent).to.have.been.calledTwice; + + RFB.messages.pointerEvent.reset(); + + // Small movement + client._handleMouseButton(13, 9, 0x001); + client._handleMouseMove(15, 14); + client._handleMouseButton(15, 14, 0x000); + expect(RFB.messages.pointerEvent).to.have.been.calledTwice; + }); + + it('should send button message directly when drag is disabled', function () { + client.dragViewport = false; + client._handleMouseButton(13, 9, 0x001); + expect(RFB.messages.pointerEvent).to.have.been.calledOnce; + }); + + it('should be initiate viewport dragging on sufficient movement', function () { + sinon.spy(client._display, "viewportChangePos"); + + // Too small movement + + client._handleMouseButton(13, 9, 0x001); + client._handleMouseMove(18, 9); + + expect(RFB.messages.pointerEvent).to.not.have.been.called; + expect(client._display.viewportChangePos).to.not.have.been.called; + + // Sufficient movement + + client._handleMouseMove(43, 9); + + expect(RFB.messages.pointerEvent).to.not.have.been.called; + expect(client._display.viewportChangePos).to.have.been.calledOnce; + expect(client._display.viewportChangePos).to.have.been.calledWith(-30, 0); + + client._display.viewportChangePos.reset(); + + // Now a small movement should move right away + + client._handleMouseMove(43, 14); + + expect(RFB.messages.pointerEvent).to.not.have.been.called; + expect(client._display.viewportChangePos).to.have.been.calledOnce; + expect(client._display.viewportChangePos).to.have.been.calledWith(0, -5); + }); + + it('should not send button messages when dragging ends', function () { + // First the movement + + client._handleMouseButton(13, 9, 0x001); + client._handleMouseMove(43, 9); + client._handleMouseButton(43, 9, 0x000); + + expect(RFB.messages.pointerEvent).to.not.have.been.called; + }); + + it('should terminate viewport dragging on a button up event', function () { + // First the dragging movement + + client._handleMouseButton(13, 9, 0x001); + client._handleMouseMove(43, 9); + client._handleMouseButton(43, 9, 0x000); + + // Another movement now should not move the viewport + + sinon.spy(client._display, "viewportChangePos"); + + client._handleMouseMove(43, 59); + + expect(client._display.viewportChangePos).to.not.have.been.called; + }); + }); + }); + + describe('Scaling', function () { + var client; + beforeEach(function () { + client = make_rfb(); + container.style.width = '70px'; + container.style.height = '80px'; + client.scaleViewport = true; + }); + + it('should update display scale factor when changing the property', function () { + var spy = sinon.spy(client._display, "scale", ["set"]); + sinon.spy(client._display, "autoscale"); + + client.scaleViewport = false; + expect(spy.set).to.have.been.calledOnce; + expect(spy.set).to.have.been.calledWith(1.0); + expect(client._display.autoscale).to.not.have.been.called; + + client.scaleViewport = true; + expect(client._display.autoscale).to.have.been.calledOnce; + expect(client._display.autoscale).to.have.been.calledWith(70, 80); + }); + + it('should update the clipping setting when changing the property', function () { + client.clipViewport = true; + + var spy = sinon.spy(client._display, "clipViewport", ["set"]); + + client.scaleViewport = false; + expect(spy.set).to.have.been.calledOnce; + expect(spy.set).to.have.been.calledWith(true); + + spy.set.reset(); + + client.scaleViewport = true; + expect(spy.set).to.have.been.calledOnce; + expect(spy.set).to.have.been.calledWith(false); + }); + + it('should update the scaling when the container size changes', function () { + sinon.spy(client._display, "autoscale"); + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(); + + expect(client._display.autoscale).to.have.been.calledOnce; + expect(client._display.autoscale).to.have.been.calledWith(40, 50); + }); + + it('should update the scaling when the remote session resizes', function () { + // Simple ExtendedDesktopSize FBU message + var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00 ]; + + sinon.spy(client._display, "autoscale"); + + client._sock._websocket._receive_data(new Uint8Array(incoming)); + + expect(client._display.autoscale).to.have.been.calledOnce; + expect(client._display.autoscale).to.have.been.calledWith(70, 80); + }); + + it('should not update the display scale factor if not scaling', function () { + client.scaleViewport = false; + + sinon.spy(client._display, "autoscale"); + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(); + + expect(client._display.autoscale).to.not.have.been.called; + }); + }); + + describe('Remote resize', function () { + var client; + beforeEach(function () { + client = make_rfb(); + client._supportsSetDesktopSize = true; + client.resizeSession = true; + container.style.width = '70px'; + container.style.height = '80px'; + sinon.spy(RFB.messages, "setDesktopSize"); + }); + + afterEach(function () { + RFB.messages.setDesktopSize.restore(); + }); + + it('should only request a resize when turned on', function () { + client.resizeSession = false; + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + client.resizeSession = true; + expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; + }); + + it('should request a resize when initially connecting', function () { + // Simple ExtendedDesktopSize FBU message + var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00 ]; + + // First message should trigger a resize + + client._supportsSetDesktopSize = false; + + client._sock._websocket._receive_data(new Uint8Array(incoming)); + + expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; + expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 70, 80, 0, 0); + + RFB.messages.setDesktopSize.reset(); + + // Second message should not trigger a resize + + client._sock._websocket._receive_data(new Uint8Array(incoming)); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + }); + + it('should request a resize when the container resizes', function () { + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(1000); + + expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; + expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0); + }); + + it('should not resize until the container size is stable', function () { + container.style.width = '20px'; + container.style.height = '30px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(400); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(400); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + + clock.tick(200); + + expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; + expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0); + }); + + it('should not resize when resize is disabled', function () { + client._resizeSession = false; + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(1000); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + }); + + it('should not resize when resize is not supported', function () { + client._supportsSetDesktopSize = false; + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(1000); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + }); + + it('should not resize when in view only mode', function () { + client._viewOnly = true; + + container.style.width = '40px'; + container.style.height = '50px'; + var event = new UIEvent('resize'); + window.dispatchEvent(event); + clock.tick(1000); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + }); + + it('should not try to override a server resize', function () { + // Simple ExtendedDesktopSize FBU message + var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00 ]; + + client._sock._websocket._receive_data(new Uint8Array(incoming)); + + expect(RFB.messages.setDesktopSize).to.not.have.been.called; + }); + }); + describe('Misc Internals', function () { describe('#_updateConnectionState', function () { var client; @@ -421,7 +818,7 @@ describe('Remote Frame Buffer Protocol Client', function() { describe('Connection States', function () { describe('connecting', function () { it('should open the websocket connection', function () { - var client = new RFB(document.createElement('canvas'), + var client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH'); sinon.spy(client._sock, 'open'); this.clock.tick(); @@ -494,7 +891,7 @@ describe('Remote Frame Buffer Protocol Client', function() { describe('disconnected', function () { var client; beforeEach(function () { - client = new RFB(document.createElement('canvas'), 'ws://HOST:8675/PATH'); + client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH'); }); it('should result in a disconnect event if state becomes "disconnected"', function () { @@ -1082,17 +1479,12 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._rfb_connection_state).to.equal('connected'); }); - it('should call the resize callback and resize the display', function () { - var spy = sinon.spy(); - client.addEventListener("fbresize", spy); + it('should resize the display', function () { sinon.spy(client._display, 'resize'); send_server_init({ width: 27, height: 32 }, client); expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(27, 32); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.width).to.equal(27); - expect(spy.args[0][0].detail.height).to.equal(32); }); it('should grab the mouse and keyboard', function () { @@ -1493,14 +1885,9 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should handle the DesktopSize pseduo-encoding', function () { var spy = sinon.spy(); - client.addEventListener("fbresize", spy); sinon.spy(client._display, 'resize'); send_fbu_msg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.width).to.equal(20); - expect(spy.args[0][0].detail.height).to.equal(50); - expect(client._fb_width).to.equal(20); expect(client._fb_height).to.equal(50); @@ -1512,14 +1899,12 @@ describe('Remote Frame Buffer Protocol Client', function() { var resizeSpy; beforeEach(function () { - client._supportsSetDesktopSize = false; // a really small frame client._fb_width = 4; client._fb_height = 4; client._display.resize(4, 4); sinon.spy(client._display, 'resize'); resizeSpy = sinon.spy(); - client.addEventListener("fbresize", resizeSpy); }); function make_screen_data (nr_of_screens) { @@ -1538,26 +1923,6 @@ describe('Remote Frame Buffer Protocol Client', function() { return data; } - it('should call callback when resize is supported', function () { - var spy = sinon.spy(); - client.addEventListener("capabilities", spy); - - expect(client._supportsSetDesktopSize).to.be.false; - expect(client.capabilities.resize).to.be.false; - - var reason_for_change = 0; // server initiated - var status_code = 0; // No error - - send_fbu_msg([{ x: reason_for_change, y: status_code, - width: 4, height: 4, encoding: -308 }], - make_screen_data(1), client); - - expect(client._supportsSetDesktopSize).to.be.true; - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.capabilities.resize).to.be.true; - expect(client.capabilities.resize).to.be.true; - }), - it('should handle a resize requested by this client', function () { var reason_for_change = 1; // requested by this client var status_code = 0; // No error @@ -1571,10 +1936,6 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(20, 50); - - expect(resizeSpy).to.have.been.calledOnce; - expect(resizeSpy.args[0][0].detail.width).to.equal(20); - expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should handle a resize requested by another client', function () { @@ -1590,10 +1951,6 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(20, 50); - - expect(resizeSpy).to.have.been.calledOnce; - expect(resizeSpy.args[0][0].detail.width).to.equal(20); - expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should be able to recieve requests which contain data for multiple screens', function () { @@ -1609,10 +1966,6 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(60, 50); - - expect(resizeSpy).to.have.been.calledOnce; - expect(resizeSpy.args[0][0].detail.width).to.equal(60); - expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should not handle a failed request', function () { @@ -1627,8 +1980,6 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._fb_height).to.equal(4); expect(client._display.resize).to.not.have.been.called; - - expect(resizeSpy).to.not.have.been.called; }); }); @@ -1808,60 +2159,6 @@ describe('Remote Frame Buffer Protocol Client', function() { RFB.messages.pointerEvent(pointer_msg, 13, 9, 0x010); expect(client._sock).to.have.sent(pointer_msg._sQ); }); - - // NB(directxman12): we don't need to test not sending messages in - // non-normal modes, since we haven't grabbed input - // yet (grabbing input should be checked in the lifecycle tests). - - it('should not send movement messages when viewport dragging', function () { - client._viewportDragging = true; - client._display.viewportChangePos = sinon.spy(); - sinon.spy(client._sock, 'flush'); - client._handleMouseMove(13, 9); - expect(client._sock.flush).to.not.have.been.called; - }); - - it('should not send button messages when initiating viewport dragging', function () { - client.dragViewport = true; - sinon.spy(client._sock, 'flush'); - client._handleMouseButton(13, 9, 0x001); - expect(client._sock.flush).to.not.have.been.called; - }); - - it('should be initiate viewport dragging on a button down event, if enabled', function () { - client.dragViewport = true; - client._handleMouseButton(13, 9, 0x001); - expect(client._viewportDragging).to.be.true; - expect(client._viewportDragPos).to.deep.equal({ x: 13, y: 9 }); - }); - - it('should terminate viewport dragging on a button up event, if enabled', function () { - client.dragViewport = true; - client._viewportDragging = true; - client._handleMouseButton(13, 9, 0x000); - expect(client._viewportDragging).to.be.false; - }); - - it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () { - var oldX = 123; - var oldY = 109; - var newX = 123 + 11 * window.devicePixelRatio; - var newY = 109 + 4 * window.devicePixelRatio; - - client.dragViewport = true; - client._viewportDragging = true; - client._viewportHasMoved = false; - client._viewportDragPos = { x: oldX, y: oldY }; - client._display.viewportChangePos = sinon.spy(); - - client._handleMouseMove(newX, newY); - - expect(client._viewportDragging).to.be.true; - expect(client._viewportHasMoved).to.be.true; - expect(client._viewportDragPos).to.deep.equal({ x: newX, y: newY }); - expect(client._display.viewportChangePos).to.have.been.calledOnce; - expect(client._display.viewportChangePos).to.have.been.calledWith(oldX - newX, oldY - newY); - }); }); describe('Keyboard Event Handlers', function () { @@ -1912,7 +2209,7 @@ describe('Remote Frame Buffer Protocol Client', function() { // open events it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () { - client = new RFB(document.createElement('canvas'), 'wss://host:8675'); + client = new RFB(document.createElement('div'), 'wss://host:8675'); this.clock.tick(); client._sock._websocket._open(); expect(client._rfb_init_state).to.equal('ProtocolVersion'); diff --git a/vnc.html b/vnc.html index fba59b1b..9c945efb 100644 --- a/vnc.html +++ b/vnc.html @@ -315,22 +315,15 @@
+
- -
- - - - - Canvas not supported. - -
- + +