diff --git a/LICENSE.txt b/LICENSE.txt index c5aa5050..2d094089 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -10,6 +10,7 @@ is not limited to): include/display.js include/input.js include/jsunzip.js + include/keysym.js include/logo.js include/rfb.js include/ui.js diff --git a/images/alt.png b/images/alt.png new file mode 100644 index 00000000..d42af7b4 Binary files /dev/null and b/images/alt.png differ diff --git a/images/ctrl.png b/images/ctrl.png new file mode 100644 index 00000000..a63b601f Binary files /dev/null and b/images/ctrl.png differ diff --git a/images/esc.png b/images/esc.png new file mode 100644 index 00000000..ece5f7cb Binary files /dev/null and b/images/esc.png differ diff --git a/images/showextrakeys.png b/images/showextrakeys.png new file mode 100644 index 00000000..ad8e0a70 Binary files /dev/null and b/images/showextrakeys.png differ diff --git a/images/tab.png b/images/tab.png new file mode 100644 index 00000000..84134872 Binary files /dev/null and b/images/tab.png differ diff --git a/include/base.css b/include/base.css index 89b2b574..f57d20e3 100644 --- a/include/base.css +++ b/include/base.css @@ -1,6 +1,7 @@ /* * noVNC base CSS * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ @@ -51,7 +52,6 @@ html { float:right; } - #noVNC_view_drag_button { display: none; } @@ -62,34 +62,36 @@ html { display: none; } +#noVNC_extra_keys { + display: inline; + list-style-type: none; + padding: 0px; + margin: 0px; + position: relative; +} + .noVNC-buttons-left { float: left; - padding-left:10px; - padding-top:4px; + z-index: 1; + position: relative; } .noVNC-buttons-right { float:right; right: 0px; - padding-right:10px; - padding-top:4px; -} - -#noVNC_status_bar { - margin-top: 0px; - padding: 0px; -} - -#noVNC_status_bar div { - font-size: 12px; - padding-top: 4px; - width:100%; + z-index: 2; + position: absolute; } #noVNC_status { - height:20px; + font-size: 12px; + padding-top: 4px; + height:32px; text-align: center; + font-weight: bold; + color: #fff; } + #noVNC_settings_menu { margin: 3px; text-align: left; @@ -104,22 +106,12 @@ html { float:right; } -.noVNC_status_normal { - background: #eee; -} -.noVNC_status_error { - background: #f44; -} -.noVNC_status_warn { - background: #ff4; -} - /* Do not set width/height for VNC_screen or VNC_canvas or incorrect * scaling will occur. Canvas resizes to remote VNC settings */ #noVNC_screen_pad { margin: 0px; padding: 0px; - height: 44px; + height: 36px; } #noVNC_screen { text-align: center; @@ -154,14 +146,14 @@ html { /*Bubble contents divs*/ #noVNC_settings { display:none; - margin-top:77px; + margin-top:73px; right:20px; position:fixed; } #noVNC_controls { display:none; - margin-top:77px; + margin-top:73px; right:12px; position:fixed; } @@ -173,7 +165,7 @@ html { display:none; position:fixed; - margin-top:77px; + margin-top:73px; right:20px; left:20px; padding:15px; @@ -186,9 +178,30 @@ html { border-radius:10px; } +#noVNC_popup_status_panel { + display:none; + position: fixed; + z-index: 1; + + margin:15px; + margin-top:60px; + padding:15px; + width:auto; + + text-align:center; + font-weight:bold; + word-wrap:break-word; + color:#fff; + background:rgba(0,0,0,0.65); + + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; +} + #noVNC_clipboard { display:none; - margin-top:77px; + margin-top:73px; right:30px; position:fixed; } @@ -207,17 +220,11 @@ html { z-index: -1; } -.noVNC_status_warn { - background-color:yellow; -} - /* * Advanced Styling */ -/* Control bar */ -#noVNC-control-bar { - position:fixed; +.noVNC_status_normal { background: #b2bdcd; /* Old browsers */ background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ @@ -225,9 +232,32 @@ html { background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} +.noVNC_status_error { + background: #f04040; /* Old browsers */ + background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} +.noVNC_status_warn { + background: #f0f040; /* Old browsers */ + background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} +/* Control bar */ +#noVNC-control-bar { + position:fixed; + display:block; - height:44px; + height:36px; left:0; top:0; width:100%; @@ -368,22 +398,85 @@ html { font-size: 180px; } -@media screen and (min-width: 481px) and (max-width: 640px) { - .noVNC_status_button { - font-size: 10px; +.noVNC-buttons-left { + padding-left: 10px; +} + +.noVNC-buttons-right { + padding-right: 10px; +} + +#noVNC_status { + z-index: 0; + position: absolute; + width: 100%; + margin-left: 0px; +} + +#showExtraKeysButton { display: none; } +#toggleCtrlButton { display: inline; } +#toggleAltButton { display: inline; } +#sendTabButton { display: inline; } +#sendEscButton { display: inline; } + +/* left-align the status text on lower resolutions */ +@media screen and (max-width: 800px){ + #noVNC_status { + z-index: 1; + position: relative; + width: auto; + float: left; + margin-left: 4px; } +} + +@media screen and (max-width: 640px){ #noVNC_clipboard_text { width: 410px; } #noVNC_logo { font-size: 150px; } -} - -@media screen and (min-width: 321px) and (max-width: 480px) { .noVNC_status_button { font-size: 10px; } + .noVNC-buttons-left { + padding-left: 0px; + } + .noVNC-buttons-right { + padding-right: 0px; + } + /* collapse the extra keys on lower resolutions */ + #showExtraKeysButton { + display: inline; + } + #toggleCtrlButton { + display: none; + position: absolute; + top: 30px; + left: 0px; + } + #toggleAltButton { + display: none; + position: absolute; + top: 65px; + left: 0px; + } + #sendTabButton { + display: none; + position: absolute; + top: 100px; + left: 0px; + } + #sendEscButton { + display: none; + position: absolute; + top: 135px; + left: 0px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { #noVNC_clipboard_text { width: 250px; } diff --git a/include/black.css b/include/black.css index 351f7b24..7d940c5a 100644 --- a/include/black.css +++ b/include/black.css @@ -1,6 +1,7 @@ /* * noVNC black CSS * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ @@ -9,7 +10,7 @@ background-color:#000; } -#noVNC-control-bar { +.noVNC_status_normal { background: #4c4c4c; /* Old browsers */ background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ @@ -18,6 +19,24 @@ background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ } +.noVNC_status_error { + background: #f04040; /* Old browsers */ + background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} +.noVNC_status_warn { + background: #f0f040; /* Old browsers */ + background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} .triangle-right { border:2px solid #fff; diff --git a/include/blue.css b/include/blue.css index 6fff89a5..b2a0adcc 100644 --- a/include/blue.css +++ b/include/blue.css @@ -1,11 +1,12 @@ /* * noVNC blue CSS * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). */ -#noVNC-control-bar { +.noVNC_status_normal { background-color:#04073d; background-image: -webkit-gradient( linear, @@ -20,6 +21,36 @@ rgb(4,7,61) 50% ); } +.noVNC_status_error { + background-color:#f04040; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(240,64,64)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(4,7,61) 54%, + rgb(249,64,64) 50% + ); +} +.noVNC_status_warn { + background-color:#f0f040; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(240,240,64)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(4,7,61) 54%, + rgb(240,240,64) 50% + ); +} .triangle-right { border:2px solid #fff; diff --git a/include/input.js b/include/input.js index b996c7d5..8f0c650f 100644 --- a/include/input.js +++ b/include/input.js @@ -1,6 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 or any later version (see LICENSE.txt) */ @@ -489,6 +490,9 @@ var that = {}, // Public API methods conf = {}, // Configuration attributes mouseCaptured = false; +var doubleClickTimer = null, + lastTouchPos = null; + // Configuration attributes Util.conf_defaults(conf, that, defaults, [ ['target', 'ro', 'dom', document, 'DOM element that captures mouse input'], @@ -521,6 +525,10 @@ function releaseMouse() { // Private functions // +function resetDoubleClickTimer() { + doubleClickTimer = null; +} + function onMouseButton(e, down) { var evt, pos, bmask; if (! conf.focused) { @@ -528,8 +536,34 @@ function onMouseButton(e, down) { } evt = (e ? e : window.event); pos = Util.getEventPosition(e, conf.target, conf.scale); + if (e.touches || e.changedTouches) { // Touch device + + // When two touches occur within 500 ms of each other and are + // closer than 20 pixels together a double click is triggered. + if (down == 1) { + if (doubleClickTimer == null) { + lastTouchPos = pos; + } else { + clearTimeout(doubleClickTimer); + + // When the distance between the two touches is small enough + // force the position of the latter touch to the position of + // the first. + + var xs = lastTouchPos.x - pos.x; + var ys = lastTouchPos.y - pos.y; + var d = Math.sqrt((xs * xs) + (ys * ys)); + + // The goal is to trigger on a certain physical width, the + // devicePixelRatio brings us a bit closer but is not optimal. + if (d < 20 * window.devicePixelRatio) { + pos = lastTouchPos; + } + } + doubleClickTimer = setTimeout(resetDoubleClickTimer, 500); + } bmask = conf.touchButton; // If bmask is set } else if (evt.which) { @@ -543,7 +577,7 @@ function onMouseButton(e, down) { } //Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down + // " bmask: " + bmask + "(evt.button: " + evt.button + ")"); - if (bmask > 0 && conf.onMouseButton) { + if (conf.onMouseButton) { Util.Debug("onMouseButton " + (down ? "down" : "up") + ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); conf.onMouseButton(pos.x, pos.y, down, bmask); @@ -611,9 +645,9 @@ function onMouseDisable(e) { evt = (e ? e : window.event); pos = Util.getEventPosition(e, conf.target, conf.scale); /* Stop propagation if inside canvas area */ - if ((pos.x >= 0) && (pos.y >= 0) && - (pos.x < conf.target.offsetWidth) && - (pos.y < conf.target.offsetHeight)) { + if ((pos.realx >= 0) && (pos.realy >= 0) && + (pos.realx < conf.target.offsetWidth) && + (pos.realy < conf.target.offsetHeight)) { //Util.Debug("mouse event disabled"); Util.stopEvent(e); return false; diff --git a/include/keysym.js b/include/keysym.js new file mode 100644 index 00000000..a00d595e --- /dev/null +++ b/include/keysym.js @@ -0,0 +1,376 @@ +var XK_VoidSymbol = 0xffffff, /* Void symbol */ + +XK_BackSpace = 0xff08, /* Back space, back char */ +XK_Tab = 0xff09, +XK_Linefeed = 0xff0a, /* Linefeed, LF */ +XK_Clear = 0xff0b, +XK_Return = 0xff0d, /* Return, enter */ +XK_Pause = 0xff13, /* Pause, hold */ +XK_Scroll_Lock = 0xff14, +XK_Sys_Req = 0xff15, +XK_Escape = 0xff1b, +XK_Delete = 0xffff, /* Delete, rubout */ + +/* Cursor control & motion */ + +XK_Home = 0xff50, +XK_Left = 0xff51, /* Move left, left arrow */ +XK_Up = 0xff52, /* Move up, up arrow */ +XK_Right = 0xff53, /* Move right, right arrow */ +XK_Down = 0xff54, /* Move down, down arrow */ +XK_Prior = 0xff55, /* Prior, previous */ +XK_Page_Up = 0xff55, +XK_Next = 0xff56, /* Next */ +XK_Page_Down = 0xff56, +XK_End = 0xff57, /* EOL */ +XK_Begin = 0xff58, /* BOL */ + + +/* Misc functions */ + +XK_Select = 0xff60, /* Select, mark */ +XK_Print = 0xff61, +XK_Execute = 0xff62, /* Execute, run, do */ +XK_Insert = 0xff63, /* Insert, insert here */ +XK_Undo = 0xff65, +XK_Redo = 0xff66, /* Redo, again */ +XK_Menu = 0xff67, +XK_Find = 0xff68, /* Find, search */ +XK_Cancel = 0xff69, /* Cancel, stop, abort, exit */ +XK_Help = 0xff6a, /* Help */ +XK_Break = 0xff6b, +XK_Mode_switch = 0xff7e, /* Character set switch */ +XK_script_switch = 0xff7e, /* Alias for mode_switch */ +XK_Num_Lock = 0xff7f, + +/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */ + +XK_KP_Space = 0xff80, /* Space */ +XK_KP_Tab = 0xff89, +XK_KP_Enter = 0xff8d, /* Enter */ +XK_KP_F1 = 0xff91, /* PF1, KP_A, ... */ +XK_KP_F2 = 0xff92, +XK_KP_F3 = 0xff93, +XK_KP_F4 = 0xff94, +XK_KP_Home = 0xff95, +XK_KP_Left = 0xff96, +XK_KP_Up = 0xff97, +XK_KP_Right = 0xff98, +XK_KP_Down = 0xff99, +XK_KP_Prior = 0xff9a, +XK_KP_Page_Up = 0xff9a +XK_KP_Next = 0xff9b, +XK_KP_Page_Down = 0xff9b, +XK_KP_End = 0xff9c, +XK_KP_Begin = 0xff9d, +XK_KP_Insert = 0xff9e, +XK_KP_Delete = 0xff9f, +XK_KP_Equal = 0xffbd, /* Equals */ +XK_KP_Multiply = 0xffaa, +XK_KP_Add = 0xffab, +XK_KP_Separator = 0xffac, /* Separator, often comma */ +XK_KP_Subtract = 0xffad, +XK_KP_Decimal = 0xffae, +XK_KP_Divide = 0xffaf, + +XK_KP_0 = 0xffb0, +XK_KP_1 = 0xffb1, +XK_KP_2 = 0xffb2, +XK_KP_3 = 0xffb3, +XK_KP_4 = 0xffb4, +XK_KP_5 = 0xffb5, +XK_KP_6 = 0xffb6, +XK_KP_7 = 0xffb7, +XK_KP_8 = 0xffb8, +XK_KP_9 = 0xffb9, + +/* + * Auxiliary functions; note the duplicate definitions for left and right + * function keys; Sun keyboards and a few other manufacturers have such + * function key groups on the left and/or right sides of the keyboard. + * We've not found a keyboard with more than 35 function keys total. + */ + +XK_F1 = 0xffbe, +XK_F2 = 0xffbf, +XK_F3 = 0xffc0, +XK_F4 = 0xffc1, +XK_F5 = 0xffc2, +XK_F6 = 0xffc3, +XK_F7 = 0xffc4, +XK_F8 = 0xffc5, +XK_F9 = 0xffc6, +XK_F10 = 0xffc7, +XK_F11 = 0xffc8, +XK_L1 = 0xffc8, +XK_F12 = 0xffc9, +XK_L2 = 0xffc9, +XK_F13 = 0xffca, +XK_L3 = 0xffca, +XK_F14 = 0xffcb, +XK_L4 = 0xffcb, +XK_F15 = 0xffcc, +XK_L5 = 0xffcc, +XK_F16 = 0xffcd, +XK_L6 = 0xffcd, +XK_F17 = 0xffce, +XK_L7 = 0xffce, +XK_F18 = 0xffcf, +XK_L8 = 0xffcf, +XK_F19 = 0xffd0, +XK_L9 = 0xffd0, +XK_F20 = 0xffd1, +XK_L10 = 0xffd1, +XK_F21 = 0xffd2, +XK_R1 = 0xffd2, +XK_F22 = 0xffd3, +XK_R2 = 0xffd3, +XK_F23 = 0xffd4, +XK_R3 = 0xffd4, +XK_F24 = 0xffd5, +XK_R4 = 0xffd5, +XK_F25 = 0xffd6, +XK_R5 = 0xffd6, +XK_F26 = 0xffd7, +XK_R6 = 0xffd7, +XK_F27 = 0xffd8, +XK_R7 = 0xffd8, +XK_F28 = 0xffd9, +XK_R8 = 0xffd9, +XK_F29 = 0xffda, +XK_R9 = 0xffda, +XK_F30 = 0xffdb, +XK_R10 = 0xffdb, +XK_F31 = 0xffdc, +XK_R11 = 0xffdc, +XK_F32 = 0xffdd, +XK_R12 = 0xffdd, +XK_F33 = 0xffde, +XK_R13 = 0xffde, +XK_F34 = 0xffdf, +XK_R14 = 0xffdf, +XK_F35 = 0xffe0, +XK_R15 = 0xffe0, + +/* Modifiers */ + +XK_Shift_L = 0xffe1, /* Left shift */ +XK_Shift_R = 0xffe2, /* Right shift */ +XK_Control_L = 0xffe3, /* Left control */ +XK_Control_R = 0xffe4, /* Right control */ +XK_Caps_Lock = 0xffe5, /* Caps lock */ +XK_Shift_Lock = 0xffe6, /* Shift lock */ + +XK_Meta_L = 0xffe7, /* Left meta */ +XK_Meta_R = 0xffe8, /* Right meta */ +XK_Alt_L = 0xffe9, /* Left alt */ +XK_Alt_R = 0xffea, /* Right alt */ +XK_Super_L = 0xffeb, /* Left super */ +XK_Super_R = 0xffec, /* Right super */ +XK_Hyper_L = 0xffed, /* Left hyper */ +XK_Hyper_R = 0xffee, /* Right hyper */ + +/* + * Latin 1 + * (ISO/IEC 8859-1 = Unicode U+0020..U+00FF) + * Byte 3 = 0 + */ + +XK_space = 0x0020, /* U+0020 SPACE */ +XK_exclam = 0x0021, /* U+0021 EXCLAMATION MARK */ +XK_quotedbl = 0x0022, /* U+0022 QUOTATION MARK */ +XK_numbersign = 0x0023, /* U+0023 NUMBER SIGN */ +XK_dollar = 0x0024, /* U+0024 DOLLAR SIGN */ +XK_percent = 0x0025, /* U+0025 PERCENT SIGN */ +XK_ampersand = 0x0026, /* U+0026 AMPERSAND */ +XK_apostrophe = 0x0027, /* U+0027 APOSTROPHE */ +XK_quoteright = 0x0027, /* deprecated */ +XK_parenleft = 0x0028, /* U+0028 LEFT PARENTHESIS */ +XK_parenright = 0x0029, /* U+0029 RIGHT PARENTHESIS */ +XK_asterisk = 0x002a, /* U+002A ASTERISK */ +XK_plus = 0x002b, /* U+002B PLUS SIGN */ +XK_comma = 0x002c, /* U+002C COMMA */ +XK_minus = 0x002d, /* U+002D HYPHEN-MINUS */ +XK_period = 0x002e, /* U+002E FULL STOP */ +XK_slash = 0x002f, /* U+002F SOLIDUS */ +XK_0 = 0x0030, /* U+0030 DIGIT ZERO */ +XK_1 = 0x0031, /* U+0031 DIGIT ONE */ +XK_2 = 0x0032, /* U+0032 DIGIT TWO */ +XK_3 = 0x0033, /* U+0033 DIGIT THREE */ +XK_4 = 0x0034, /* U+0034 DIGIT FOUR */ +XK_5 = 0x0035, /* U+0035 DIGIT FIVE */ +XK_6 = 0x0036, /* U+0036 DIGIT SIX */ +XK_7 = 0x0037, /* U+0037 DIGIT SEVEN */ +XK_8 = 0x0038, /* U+0038 DIGIT EIGHT */ +XK_9 = 0x0039, /* U+0039 DIGIT NINE */ +XK_colon = 0x003a, /* U+003A COLON */ +XK_semicolon = 0x003b, /* U+003B SEMICOLON */ +XK_less = 0x003c, /* U+003C LESS-THAN SIGN */ +XK_equal = 0x003d, /* U+003D EQUALS SIGN */ +XK_greater = 0x003e, /* U+003E GREATER-THAN SIGN */ +XK_question = 0x003f, /* U+003F QUESTION MARK */ +XK_at = 0x0040, /* U+0040 COMMERCIAL AT */ +XK_A = 0x0041, /* U+0041 LATIN CAPITAL LETTER A */ +XK_B = 0x0042, /* U+0042 LATIN CAPITAL LETTER B */ +XK_C = 0x0043, /* U+0043 LATIN CAPITAL LETTER C */ +XK_D = 0x0044, /* U+0044 LATIN CAPITAL LETTER D */ +XK_E = 0x0045, /* U+0045 LATIN CAPITAL LETTER E */ +XK_F = 0x0046, /* U+0046 LATIN CAPITAL LETTER F */ +XK_G = 0x0047, /* U+0047 LATIN CAPITAL LETTER G */ +XK_H = 0x0048, /* U+0048 LATIN CAPITAL LETTER H */ +XK_I = 0x0049, /* U+0049 LATIN CAPITAL LETTER I */ +XK_J = 0x004a, /* U+004A LATIN CAPITAL LETTER J */ +XK_K = 0x004b, /* U+004B LATIN CAPITAL LETTER K */ +XK_L = 0x004c, /* U+004C LATIN CAPITAL LETTER L */ +XK_M = 0x004d, /* U+004D LATIN CAPITAL LETTER M */ +XK_N = 0x004e, /* U+004E LATIN CAPITAL LETTER N */ +XK_O = 0x004f, /* U+004F LATIN CAPITAL LETTER O */ +XK_P = 0x0050, /* U+0050 LATIN CAPITAL LETTER P */ +XK_Q = 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */ +XK_R = 0x0052, /* U+0052 LATIN CAPITAL LETTER R */ +XK_S = 0x0053, /* U+0053 LATIN CAPITAL LETTER S */ +XK_T = 0x0054, /* U+0054 LATIN CAPITAL LETTER T */ +XK_U = 0x0055, /* U+0055 LATIN CAPITAL LETTER U */ +XK_V = 0x0056, /* U+0056 LATIN CAPITAL LETTER V */ +XK_W = 0x0057, /* U+0057 LATIN CAPITAL LETTER W */ +XK_X = 0x0058, /* U+0058 LATIN CAPITAL LETTER X */ +XK_Y = 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */ +XK_Z = 0x005a, /* U+005A LATIN CAPITAL LETTER Z */ +XK_bracketleft = 0x005b, /* U+005B LEFT SQUARE BRACKET */ +XK_backslash = 0x005c, /* U+005C REVERSE SOLIDUS */ +XK_bracketright = 0x005d, /* U+005D RIGHT SQUARE BRACKET */ +XK_asciicircum = 0x005e, /* U+005E CIRCUMFLEX ACCENT */ +XK_underscore = 0x005f, /* U+005F LOW LINE */ +XK_grave = 0x0060, /* U+0060 GRAVE ACCENT */ +XK_quoteleft = 0x0060, /* deprecated */ +XK_a = 0x0061, /* U+0061 LATIN SMALL LETTER A */ +XK_b = 0x0062, /* U+0062 LATIN SMALL LETTER B */ +XK_c = 0x0063, /* U+0063 LATIN SMALL LETTER C */ +XK_d = 0x0064, /* U+0064 LATIN SMALL LETTER D */ +XK_e = 0x0065, /* U+0065 LATIN SMALL LETTER E */ +XK_f = 0x0066, /* U+0066 LATIN SMALL LETTER F */ +XK_g = 0x0067, /* U+0067 LATIN SMALL LETTER G */ +XK_h = 0x0068, /* U+0068 LATIN SMALL LETTER H */ +XK_i = 0x0069, /* U+0069 LATIN SMALL LETTER I */ +XK_j = 0x006a, /* U+006A LATIN SMALL LETTER J */ +XK_k = 0x006b, /* U+006B LATIN SMALL LETTER K */ +XK_l = 0x006c, /* U+006C LATIN SMALL LETTER L */ +XK_m = 0x006d, /* U+006D LATIN SMALL LETTER M */ +XK_n = 0x006e, /* U+006E LATIN SMALL LETTER N */ +XK_o = 0x006f, /* U+006F LATIN SMALL LETTER O */ +XK_p = 0x0070, /* U+0070 LATIN SMALL LETTER P */ +XK_q = 0x0071, /* U+0071 LATIN SMALL LETTER Q */ +XK_r = 0x0072, /* U+0072 LATIN SMALL LETTER R */ +XK_s = 0x0073, /* U+0073 LATIN SMALL LETTER S */ +XK_t = 0x0074, /* U+0074 LATIN SMALL LETTER T */ +XK_u = 0x0075, /* U+0075 LATIN SMALL LETTER U */ +XK_v = 0x0076, /* U+0076 LATIN SMALL LETTER V */ +XK_w = 0x0077, /* U+0077 LATIN SMALL LETTER W */ +XK_x = 0x0078, /* U+0078 LATIN SMALL LETTER X */ +XK_y = 0x0079, /* U+0079 LATIN SMALL LETTER Y */ +XK_z = 0x007a, /* U+007A LATIN SMALL LETTER Z */ +XK_braceleft = 0x007b, /* U+007B LEFT CURLY BRACKET */ +XK_bar = 0x007c, /* U+007C VERTICAL LINE */ +XK_braceright = 0x007d, /* U+007D RIGHT CURLY BRACKET */ +XK_asciitilde = 0x007e, /* U+007E TILDE */ + +XK_nobreakspace = 0x00a0, /* U+00A0 NO-BREAK SPACE */ +XK_exclamdown = 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */ +XK_cent = 0x00a2, /* U+00A2 CENT SIGN */ +XK_sterling = 0x00a3, /* U+00A3 POUND SIGN */ +XK_currency = 0x00a4, /* U+00A4 CURRENCY SIGN */ +XK_yen = 0x00a5, /* U+00A5 YEN SIGN */ +XK_brokenbar = 0x00a6, /* U+00A6 BROKEN BAR */ +XK_section = 0x00a7, /* U+00A7 SECTION SIGN */ +XK_diaeresis = 0x00a8, /* U+00A8 DIAERESIS */ +XK_copyright = 0x00a9, /* U+00A9 COPYRIGHT SIGN */ +XK_ordfeminine = 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */ +XK_guillemotleft = 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ +XK_notsign = 0x00ac, /* U+00AC NOT SIGN */ +XK_hyphen = 0x00ad, /* U+00AD SOFT HYPHEN */ +XK_registered = 0x00ae, /* U+00AE REGISTERED SIGN */ +XK_macron = 0x00af, /* U+00AF MACRON */ +XK_degree = 0x00b0, /* U+00B0 DEGREE SIGN */ +XK_plusminus = 0x00b1, /* U+00B1 PLUS-MINUS SIGN */ +XK_twosuperior = 0x00b2, /* U+00B2 SUPERSCRIPT TWO */ +XK_threesuperior = 0x00b3, /* U+00B3 SUPERSCRIPT THREE */ +XK_acute = 0x00b4, /* U+00B4 ACUTE ACCENT */ +XK_mu = 0x00b5, /* U+00B5 MICRO SIGN */ +XK_paragraph = 0x00b6, /* U+00B6 PILCROW SIGN */ +XK_periodcentered = 0x00b7, /* U+00B7 MIDDLE DOT */ +XK_cedilla = 0x00b8, /* U+00B8 CEDILLA */ +XK_onesuperior = 0x00b9, /* U+00B9 SUPERSCRIPT ONE */ +XK_masculine = 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */ +XK_guillemotright = 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ +XK_onequarter = 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */ +XK_onehalf = 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */ +XK_threequarters = 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */ +XK_questiondown = 0x00bf, /* U+00BF INVERTED QUESTION MARK */ +XK_Agrave = 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */ +XK_Aacute = 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */ +XK_Acircumflex = 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ +XK_Atilde = 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */ +XK_Adiaeresis = 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */ +XK_Aring = 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */ +XK_AE = 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */ +XK_Ccedilla = 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */ +XK_Egrave = 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */ +XK_Eacute = 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */ +XK_Ecircumflex = 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ +XK_Ediaeresis = 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */ +XK_Igrave = 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */ +XK_Iacute = 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */ +XK_Icircumflex = 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ +XK_Idiaeresis = 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */ +XK_ETH = 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */ +XK_Eth = 0x00d0, /* deprecated */ +XK_Ntilde = 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */ +XK_Ograve = 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */ +XK_Oacute = 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */ +XK_Ocircumflex = 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ +XK_Otilde = 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */ +XK_Odiaeresis = 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */ +XK_multiply = 0x00d7, /* U+00D7 MULTIPLICATION SIGN */ +XK_Oslash = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ +XK_Ooblique = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ +XK_Ugrave = 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */ +XK_Uacute = 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */ +XK_Ucircumflex = 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ +XK_Udiaeresis = 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */ +XK_Yacute = 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */ +XK_THORN = 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */ +XK_Thorn = 0x00de, /* deprecated */ +XK_ssharp = 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */ +XK_agrave = 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */ +XK_aacute = 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */ +XK_acircumflex = 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ +XK_atilde = 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */ +XK_adiaeresis = 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */ +XK_aring = 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */ +XK_ae = 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */ +XK_ccedilla = 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */ +XK_egrave = 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */ +XK_eacute = 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */ +XK_ecircumflex = 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */ +XK_ediaeresis = 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */ +XK_igrave = 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */ +XK_iacute = 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */ +XK_icircumflex = 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */ +XK_idiaeresis = 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */ +XK_eth = 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */ +XK_ntilde = 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */ +XK_ograve = 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */ +XK_oacute = 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */ +XK_ocircumflex = 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ +XK_otilde = 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */ +XK_odiaeresis = 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */ +XK_division = 0x00f7, /* U+00F7 DIVISION SIGN */ +XK_oslash = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ +XK_ooblique = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ +XK_ugrave = 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */ +XK_uacute = 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */ +XK_ucircumflex = 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */ +XK_udiaeresis = 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */ +XK_yacute = 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */ +XK_thorn = 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */ +XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ diff --git a/include/rfb.js b/include/rfb.js index d9137370..98328779 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -1,6 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -102,7 +103,6 @@ var that = {}, // Public API methods fb_height = 0, fb_name = "", - last_req_time = 0, rre_chunk_sz = 100, timing = { @@ -147,9 +147,6 @@ Util.conf_defaults(conf, that, defaults, [ ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'], - ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'], - ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'], - // Callback functions ['onUpdateState', 'rw', 'func', function() { }, 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '], @@ -165,6 +162,8 @@ Util.conf_defaults(conf, that, defaults, [ 'onFBUComplete(rfb, fbu): RFB FBU received and processed '], ['onFBResize', 'rw', 'func', function() { }, 'onFBResize(rfb, width, height): frame buffer resized'], + ['onDesktopName', 'rw', 'func', function() { }, + 'onDesktopName(rfb, name): desktop name received'], // These callback names are deprecated ['updateState', 'rw', 'func', function() { }, @@ -400,7 +399,7 @@ updateState = function(state, statusMsg) { } if (msgTimer) { - clearInterval(msgTimer); + clearTimeout(msgTimer); msgTimer = null; } @@ -441,13 +440,13 @@ updateState = function(state, statusMsg) { if (connTimer && (rfb_state !== 'connect')) { Util.Debug("Clearing connect timer"); - clearInterval(connTimer); + clearTimeout(connTimer); connTimer = null; } if (disconnTimer && (rfb_state !== 'disconnect')) { Util.Debug("Clearing disconnect timer"); - clearInterval(disconnTimer); + clearTimeout(disconnTimer); disconnTimer = null; } @@ -566,44 +565,18 @@ function genDES(password, challenge) { return (new DES(passwd)).encrypt(challenge); } -function flushClient() { - if (mouse_arr.length > 0) { - //send(mouse_arr.concat(fbUpdateRequests())); - ws.send(mouse_arr); - setTimeout(function() { - ws.send(fbUpdateRequests()); - }, 50); - - mouse_arr = []; - return true; - } else { - return false; - } -} - // overridable for testing checkEvents = function() { - var now; - if (rfb_state === 'normal' && !viewportDragging) { - if (! flushClient()) { - now = new Date().getTime(); - if (now > last_req_time + conf.fbu_req_rate) { - last_req_time = now; - ws.send(fbUpdateRequests()); - } - } + if (rfb_state === 'normal' && !viewportDragging && mouse_arr.length > 0) { + ws.send(mouse_arr); + mouse_arr = []; } - setTimeout(checkEvents, conf.check_rate); }; keyPress = function(keysym, down) { - var arr; - if (conf.view_only) { return; } // View only, skip keyboard events - arr = keyEvent(keysym, down); - arr = arr.concat(fbUpdateRequests()); - ws.send(arr); + ws.send(keyEvent(keysym, down)); }; mouseButton = function(x, y, down, bmask) { @@ -622,7 +595,6 @@ mouseButton = function(x, y, down, bmask) { return; } else { viewportDragging = false; - ws.send(fbUpdateRequests()); // Force immediate redraw } } @@ -630,7 +602,8 @@ mouseButton = function(x, y, down, bmask) { mouse_arr = mouse_arr.concat( pointerEvent(display.absX(x), display.absY(y)) ); - flushClient(); + ws.send(mouse_arr); + mouse_arr = []; }; mouseMove = function(x, y) { @@ -653,7 +626,9 @@ mouseMove = function(x, y) { if (conf.view_only) { return; } // View only, skip mouse events mouse_arr = mouse_arr.concat( - pointerEvent(display.absX(x), display.absY(y)) ); + pointerEvent(display.absX(x), display.absY(y))); + + checkEvents(); }; @@ -873,6 +848,7 @@ init_msg = function() { /* Connection name/title */ name_length = ws.rQshift32(); fb_name = ws.rQshiftStr(name_length); + conf.onDesktopName(that, fb_name); if (conf.true_color && fb_name === "Intel(r) AMT KVM") { @@ -896,13 +872,12 @@ init_msg = function() { response = pixelFormat(); response = response.concat(clientEncodings()); - response = response.concat(fbUpdateRequests()); + response = response.concat(fbUpdateRequests()); // initial fbu-request timing.fbu_rt_start = (new Date()).getTime(); timing.pixels = 0; ws.send(response); - /* Start pushing/polling */ - setTimeout(checkEvents, conf.check_rate); + checkEvents(); if (conf.encrypt) { updateState('normal', "Connected (encrypted) to: " + fb_name); @@ -930,6 +905,10 @@ normal_msg = function() { switch (msg_type) { case 0: // FramebufferUpdate ret = framebufferUpdate(); // false means need more data + if (ret) { + // only allow one outstanding fbu-request at a time + ws.send(fbUpdateRequests()); + } break; case 1: // SetColourMapEntries Util.Debug("SetColourMapEntries"); @@ -1592,8 +1571,6 @@ encHandlers.DesktopSize = function set_desktopsize() { conf.onFBResize(that, fb_width, fb_height); display.resize(fb_width, fb_height); timing.fbu_rt_start = (new Date()).getTime(); - // Send a new non-incremental request - ws.send(fbUpdateRequests()); FBU.bytes = 0; FBU.rects -= 1; @@ -1819,7 +1796,6 @@ that.sendCtrlAltDel = function() { arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt arr = arr.concat(keyEvent(0xFFE3, 0)); // Control - arr = arr.concat(fbUpdateRequests()); ws.send(arr); }; @@ -1836,7 +1812,6 @@ that.sendKey = function(code, down) { arr = arr.concat(keyEvent(code, 1)); arr = arr.concat(keyEvent(code, 0)); } - arr = arr.concat(fbUpdateRequests()); ws.send(arr); }; diff --git a/include/ui.js b/include/ui.js index 3387567a..40972cc0 100644 --- a/include/ui.js +++ b/include/ui.js @@ -1,6 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -13,15 +14,22 @@ // Load supporting scripts window.onscriptsload = function () { UI.load(); }; Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js", - "input.js", "display.js", "jsunzip.js", "rfb.js"]); + "input.js", "display.js", "jsunzip.js", "rfb.js", + "keysym.js"]); var UI = { rfb_state : 'loaded', settingsOpen : false, connSettingsOpen : false, +popupStatusOpen : false, clipboardOpen: false, keyboardVisible: false, +hideKeyboardTimeout: null, +extraKeysVisible: false, +ctrlOn: false, +altOn: false, +isTouchDevice: false, // Setup rfb object, load settings from browser storage, then call // UI.init to setup the UI/menus @@ -31,7 +39,9 @@ load: function (callback) { // Render default UI and initialize settings menu start: function(callback) { - var html = '', i, sheet, sheets, llevels, port; + var html = '', i, sheet, sheets, llevels, port, autoconnect; + + UI.isTouchDevice = 'ontouchstart' in document.documentElement; // Stylesheet selection dropdown sheet = WebUtil.selectStylesheet(); @@ -73,7 +83,7 @@ start: function(callback) { UI.initSetting('password', ''); UI.initSetting('encrypt', (window.location.protocol === "https:")); UI.initSetting('true_color', true); - UI.initSetting('cursor', false); + UI.initSetting('cursor', !UI.isTouchDevice); UI.initSetting('shared', true); UI.initSetting('view_only', false); UI.initSetting('connectTimeout', 2); @@ -82,7 +92,17 @@ start: function(callback) { UI.rfb = RFB({'target': $D('noVNC_canvas'), 'onUpdateState': UI.updateState, - 'onClipboard': UI.clipReceive}); + 'onClipboard': UI.clipReceive, + 'onDesktopName': UI.updateDocumentTitle}); + + autoconnect = WebUtil.getQueryVar('autoconnect', false); + if (autoconnect === 'true' || autoconnect == '1') { + autoconnect = true; + UI.connect(); + } else { + autoconnect = false; + } + UI.updateVisualState(); // Unfocus clipboard when over the VNC area @@ -94,7 +114,7 @@ start: function(callback) { // }; // Show mouse selector buttons on touch screen devices - if ('ontouchstart' in document.documentElement) { + if (UI.isTouchDevice) { // Show mobile buttons $D('noVNC_mobile_buttons').style.display = "inline"; UI.setMouseButton(); @@ -132,8 +152,10 @@ start: function(callback) { // Open the description dialog $D('noVNC_description').style.display = "block"; } else { - // Open the connect panel on first load - UI.toggleConnectPanel(); + // Show the connect panel on first load unless autoconnecting + if (autoconnect === UI.connSettingsOpen) { + UI.toggleConnectPanel(); + } } // Add mouse event click/focus/blur event handlers to the UI @@ -152,10 +174,19 @@ addMouseHandlers: function() { $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); }; $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); }; $D("showKeyboard").onclick = UI.showKeyboard; - //$D("keyboardinput").onkeydown = function (event) { onKeyDown(event); }; + + $D("keyboardinput").oninput = UI.keyInput; $D("keyboardinput").onblur = UI.keyInputBlur; + $D("showExtraKeysButton").onclick = UI.showExtraKeys; + $D("toggleCtrlButton").onclick = UI.toggleCtrl; + $D("toggleAltButton").onclick = UI.toggleAlt; + $D("sendTabButton").onclick = UI.sendTab; + $D("sendEscButton").onclick = UI.sendEsc; + $D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel; + $D("noVNC_status").onclick = UI.togglePopupStatusPanel; + $D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel; $D("clipboardButton").onclick = UI.toggleClipboardPanel; $D("settingsButton").onclick = UI.toggleSettingsPanel; $D("connectButton").onclick = UI.toggleConnectPanel; @@ -257,20 +288,39 @@ forceSetting: function(name, val) { }, +// Show the popup status panel +togglePopupStatusPanel: function() { + var psp = $D('noVNC_popup_status_panel'); + if (UI.popupStatusOpen === true) { + psp.style.display = "none"; + UI.popupStatusOpen = false; + } else { + psp.innerHTML = $D('noVNC_status').innerHTML; + psp.style.display = "block"; + psp.style.left = window.innerWidth/2 - + parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px"; + UI.popupStatusOpen = true; + } +}, + // Show the clipboard panel toggleClipboardPanel: function() { // Close the description panel $D('noVNC_description').style.display = "none"; - //Close settings if open + // Close settings if open if (UI.settingsOpen === true) { UI.settingsApply(); UI.closeSettingsMenu(); } - //Close connection settings if open + // Close connection settings if open if (UI.connSettingsOpen === true) { UI.toggleConnectPanel(); } - //Toggle Clipboard Panel + // Close popup status panel if open + if (UI.popupStatusOpen === true) { + UI.togglePopupStatusPanel(); + } + // Toggle Clipboard Panel if (UI.clipboardOpen === true) { $D('noVNC_clipboard').style.display = "none"; $D('clipboardButton').className = "noVNC_status_button"; @@ -286,17 +336,22 @@ toggleClipboardPanel: function() { toggleConnectPanel: function() { // Close the description panel $D('noVNC_description').style.display = "none"; - //Close connection settings if open + // Close connection settings if open if (UI.settingsOpen === true) { UI.settingsApply(); UI.closeSettingsMenu(); $D('connectButton').className = "noVNC_status_button"; } + // Close clipboard panel if open if (UI.clipboardOpen === true) { UI.toggleClipboardPanel(); } + // Close popup status panel if open + if (UI.popupStatusOpen === true) { + UI.togglePopupStatusPanel(); + } - //Toggle Connection Panel + // Toggle Connection Panel if (UI.connSettingsOpen === true) { $D('noVNC_controls').style.display = "none"; $D('connectButton').className = "noVNC_status_button"; @@ -327,7 +382,7 @@ toggleSettingsPanel: function() { if (UI.rfb.get_display().get_cursor_uri()) { UI.updateSetting('cursor'); } else { - UI.updateSetting('cursor', false); + UI.updateSetting('cursor', !UI.isTouchDevice); $D('noVNC_cursor').disabled = true; } UI.updateSetting('clip'); @@ -347,13 +402,18 @@ toggleSettingsPanel: function() { openSettingsMenu: function() { // Close the description panel $D('noVNC_description').style.display = "none"; + // Close clipboard panel if open if (UI.clipboardOpen === true) { UI.toggleClipboardPanel(); } - //Close connection settings if open + // Close connection settings if open if (UI.connSettingsOpen === true) { UI.toggleConnectPanel(); } + // Close popup status panel if open + if (UI.popupStatusOpen === true) { + UI.togglePopupStatusPanel(); + } $D('noVNC_settings').style.display = "block"; $D('settingsButton').className = "noVNC_status_button_selected"; UI.settingsOpen = true; @@ -437,8 +497,6 @@ setMouseButton: function(num) { updateState: function(rfb, state, oldstate, msg) { var s, sb, c, d, cad, vd, klass; UI.rfb_state = state; - s = $D('noVNC_status'); - sb = $D('noVNC_status_bar'); switch (state) { case 'failed': case 'fatal': @@ -468,9 +526,8 @@ updateState: function(rfb, state, oldstate, msg) { } if (typeof(msg) !== 'undefined') { - s.setAttribute("class", klass); - sb.setAttribute("class", klass); - s.innerHTML = msg; + $D('noVNC-control-bar').setAttribute("class", klass); + $D('noVNC_status').innerHTML = msg; } UI.updateVisualState(); @@ -487,7 +544,7 @@ updateVisualState: function() { UI.rfb.get_display().get_cursor_uri()) { $D('noVNC_cursor').disabled = connected; } else { - UI.updateSetting('cursor', false); + UI.updateSetting('cursor', !UI.isTouchDevice); $D('noVNC_cursor').disabled = true; } $D('noVNC_shared').disabled = connected; @@ -501,13 +558,16 @@ updateVisualState: function() { UI.setMouseButton(1); $D('clipboardButton').style.display = "inline"; $D('showKeyboard').style.display = "inline"; + $D('noVNC_extra_keys').style.display = ""; $D('sendCtrlAltDelButton').style.display = "inline"; } else { UI.setMouseButton(); $D('clipboardButton').style.display = "none"; $D('showKeyboard').style.display = "none"; + $D('noVNC_extra_keys').style.display = "none"; $D('sendCtrlAltDelButton').style.display = "none"; } + // State change disables viewport dragging. // It is enabled (toggled) by direct click on the button UI.setViewDrag(false); @@ -530,6 +590,12 @@ updateVisualState: function() { }, +// Display the desktop name in the document title +updateDocumentTitle: function(rfb, name) { + document.title = name + " - noVNC"; +}, + + clipReceive: function(rfb, text) { Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); $D('noVNC_clipboard_text').value = text; @@ -664,23 +730,123 @@ setViewDrag: function(drag) { // On touch devices, show the OS keyboard showKeyboard: function() { + var kbi, skb, l; + kbi = $D('keyboardinput'); + skb = $D('showKeyboard'); + l = kbi.value.length; if(UI.keyboardVisible === false) { - $D('keyboardinput').focus(); + kbi.focus(); + kbi.setSelectionRange(l, l); // Move the caret to the end UI.keyboardVisible = true; - $D('showKeyboard').className = "noVNC_status_button_selected"; + skb.className = "noVNC_status_button_selected"; } else if(UI.keyboardVisible === true) { - $D('keyboardinput').blur(); - $D('showKeyboard').className = "noVNC_status_button"; + kbi.blur(); + skb.className = "noVNC_status_button"; UI.keyboardVisible = false; } }, +keepKeyboard: function() { + clearTimeout(UI.hideKeyboardTimeout); + if(UI.keyboardVisible === true) { + $D('keyboardinput').focus(); + $D('showKeyboard').className = "noVNC_status_button_selected"; + } else if(UI.keyboardVisible === false) { + $D('keyboardinput').blur(); + $D('showKeyboard').className = "noVNC_status_button"; + } +}, + +// When keypress events are left uncought, catch the input events from +// the keyboardinput element instead and send the corresponding key events. +keyInput: function(event) { + var elem, input, len; + elem = $D('keyboardinput'); + input = event.target.value; + len = (elem.selectionStart > input.length) ? elem.selectionStart : input.length; + + if (len < 1) { // something removed? + UI.rfb.sendKey(0xff08); // send BACKSPACE + } else if (len > 1) { // new input? + for (var i = len-1; i > 0; i -= 1) { + // HTML does not consider trailing whitespaces as a part of the string + // and they are therefore undefined. + if (input[len-i] !== undefined) { + UI.rfb.sendKey(input.charCodeAt(len-i)); // send charCode + } else { + UI.rfb.sendKey(0x0020); // send SPACE + } + } + } + + // In order to be able to delete text which has been written in + // another session there has to always be text in the + // keyboardinput element with which backspace can interact. + // We also need to reset the input field text to avoid overflow. + elem.value = "x"; +}, + keyInputBlur: function() { $D('showKeyboard').className = "noVNC_status_button"; //Weird bug in iOS if you change keyboardVisible //here it does not actually occur so next time //you click keyboard icon it doesnt work. - setTimeout(function() { UI.setKeyboard(); },100); + UI.hideKeyboardTimeout = setTimeout(function() { UI.setKeyboard(); },100); +}, + +showExtraKeys: function() { + UI.keepKeyboard(); + if(UI.extraKeysVisible === false) { + $D('toggleCtrlButton').style.display = "inline"; + $D('toggleAltButton').style.display = "inline"; + $D('sendTabButton').style.display = "inline"; + $D('sendEscButton').style.display = "inline"; + $D('showExtraKeysButton').className = "noVNC_status_button_selected"; + UI.extraKeysVisible = true; + } else if(UI.extraKeysVisible === true) { + $D('toggleCtrlButton').style.display = ""; + $D('toggleAltButton').style.display = ""; + $D('sendTabButton').style.display = ""; + $D('sendEscButton').style.display = ""; + $D('showExtraKeysButton').className = "noVNC_status_button"; + UI.extraKeysVisible = false; + } +}, + +toggleCtrl: function() { + UI.keepKeyboard(); + if(UI.ctrlOn === false) { + UI.rfb.sendKey(XK_Control_L, true); + $D('toggleCtrlButton').className = "noVNC_status_button_selected"; + UI.ctrlOn = true; + } else if(UI.ctrlOn === true) { + UI.rfb.sendKey(XK_Control_L, false); + $D('toggleCtrlButton').className = "noVNC_status_button"; + UI.ctrlOn = false; + } +}, + +toggleAlt: function() { + UI.keepKeyboard(); + if(UI.altOn === false) { + UI.rfb.sendKey(XK_Alt_L, true); + $D('toggleAltButton').className = "noVNC_status_button_selected"; + UI.altOn = true; + } else if(UI.altOn === true) { + UI.rfb.sendKey(XK_Alt_L, false); + $D('toggleAltButton').className = "noVNC_status_button"; + UI.altOn = false; + } +}, + +sendTab: function() { + UI.keepKeyboard(); + UI.rfb.sendKey(XK_Tab); +}, + +sendEsc: function() { + UI.keepKeyboard(); + UI.rfb.sendKey(XK_Escape); }, setKeyboard: function() { diff --git a/include/util.js b/include/util.js index dd1f252f..8893591c 100644 --- a/include/util.js +++ b/include/util.js @@ -298,9 +298,11 @@ Util.getEventPosition = function (e, obj, scale) { if (typeof scale === "undefined") { scale = 1; } - var x = Math.max(Math.min(docX - pos.x, obj.width-1), 0); - var y = Math.max(Math.min(docY - pos.y, obj.height-1), 0); - return {'x': x / scale, 'y': y / scale}; + var realx = docX - pos.x; + var realy = docY - pos.y; + var x = Math.max(Math.min(realx, obj.width-1), 0); + var y = Math.max(Math.min(realy, obj.height-1), 0); + return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale}; }; diff --git a/vnc.html b/vnc.html index 5f6cc70e..8b8847cc 100644 --- a/vnc.html +++ b/vnc.html @@ -5,6 +5,7 @@
+ +