diff --git a/install.sh b/install.sh index 91f099d2ad..02349dcabb 100755 --- a/install.sh +++ b/install.sh @@ -2612,7 +2612,6 @@ SUNSTONE_PUBLIC_IMAGES_FILES="src/sunstone/public/images/ajax-loader.gif \ src/sunstone/public/images/opennebula-5.0.png \ src/sunstone/public/images/opennebula-sunstone-v4.0.png \ src/sunstone/public/images/opennebula-sunstone-v4.14-small.png \ - src/sunstone/public/images/one_small_logo.png \ src/sunstone/public/images/panel.png \ src/sunstone/public/images/panel_short.png \ src/sunstone/public/images/pbar.gif \ diff --git a/src/sunstone/public/app/console/guacamole.js b/src/sunstone/public/app/console/guacamole.js index 84e21093bb..4940c820c7 100644 --- a/src/sunstone/public/app/console/guacamole.js +++ b/src/sunstone/public/app/console/guacamole.js @@ -15,24 +15,57 @@ /* -------------------------------------------------------------------------- */ define(function(require) { + require("jquery-ui"); + var GuacController = require('utils/guacamole/controller'); - - try { - var endpoint = new URL(window.location.href); - var encoded_socket = endpoint.searchParams.get("socket"); - var socket_string = atob(encoded_socket); - - var url = new URL(socket_string); - var params = url.searchParams; - var token = params.get("token"); - var info = params.get("info"); - var controller = new GuacController(); - controller.setInformation(info); - controller.setConnection(token); - } catch (error) { - console.log(error); - $('#guacamole-state').empty().text('Failed'); + var controller = new GuacController(); + var reconnectButton = document.getElementById('buttons__reconnect'); + + var endpoint = new URL(window.location.href); + var encoded_socket = endpoint.searchParams.get("socket"); + var socket_string = atob(encoded_socket); + + var url = new URL(socket_string); + var params = url.searchParams; + var token = params.get("token"); + var connectionType = params.get("type"); + var info = params.get("info"); + + controller.setInformation(info); + + // Trigger first connect + document.readyState !== 'loading' + ? connect() + : document.addEventListener('DOMContentLoaded', connect); + + window.onunload = disconnect; + + + reconnectButton.onclick = function reconnect() { + disconnect(); + + document.querySelector('.toolbar__state h5').innerHTML = ""; + document.querySelector('.toolbar__state .spinner').style.display = "block"; + + setTimeout(connect, 500) + } + + function connect() { + try { + controller && controller.setConnection(token, connectionType); + } catch (error) { + controller && controller.disconnect(); + document.querySelector('.toolbar__state h5').innerHTML = "Failed"; + } + } + + function disconnect() { + try { + controller && controller.disconnect(); + } catch (error) { + document.querySelector('.toolbar__state h5').innerHTML = "Failed"; + } } }); diff --git a/src/sunstone/public/app/sunstone-config.js b/src/sunstone/public/app/sunstone-config.js index 7cf57f3b53..4fc7935546 100644 --- a/src/sunstone/public/app/sunstone-config.js +++ b/src/sunstone/public/app/sunstone-config.js @@ -172,14 +172,14 @@ define(function(require) { } } }, - "logo": (_config["view"]["provision_logo"] || "images/one_small_logo.png"), + "logo": (_config["view"]["provision_logo"] || "images/opennebula-5.0.png"), }, "tableOrder": _config["user_config"]["table_order"], "vncProxyPort": _config["system_config"]["vnc_client_port"] || _config["system_config"]["vnc_proxy_port"].split(":")[1] || _config["system_config"]["vnc_proxy_port"], "vncWSS": _config["user_config"]["vnc_wss"], "requestVNCPassword": _config["system_config"]["vnc_request_password"], - "logo": (_config["view"]["small_logo"] || "images/one_small_logo.png"), + "logo": (_config["view"]["small_logo"] || "images/opennebula-5.0.png"), "link_logo": (_config["view"]["link_logo"] || false), "text_link_logo": (_config["view"]["text_link_logo"] || false), "vmLogos": (_config["vm_logos"]), diff --git a/src/sunstone/public/app/tabs/vms-tab/actions.js b/src/sunstone/public/app/tabs/vms-tab/actions.js index 37411c1e30..d430d1f421 100644 --- a/src/sunstone/public/app/tabs/vms-tab/actions.js +++ b/src/sunstone/public/app/tabs/vms-tab/actions.js @@ -371,10 +371,13 @@ define(function(require) { "VM.startguac_action" : { type: "single", call: OpenNebulaVM.guac, - callback: function(_, response) { - var link = RemoteActions.getLink(response, { + callback: function(request, response) { + var protocolConnection = request.request.data[0].extra_param; + + var link = getLink(response, { connnection_type: 'guac', - extra_path: '/fireedge/guacamole' + extra_path: '/fireedge/guacamole', + extra_params: ['type=' + protocolConnection] }); // Open in a new tab the noVNC connection window.open(link); diff --git a/src/sunstone/public/app/utils/guacamole/controller.js b/src/sunstone/public/app/utils/guacamole/controller.js index 53b4d008c7..39e8148749 100644 --- a/src/sunstone/public/app/utils/guacamole/controller.js +++ b/src/sunstone/public/app/utils/guacamole/controller.js @@ -16,53 +16,45 @@ define(function(require) { - require("guacamole-common-js") - var ManagedClient = require("utils/guacamole/types/client"); - var ManagedClientState = require("utils/guacamole/types/client-state"); - var Utils = require("utils/guacamole/utils"); - var UtilsConnection = require("utils/info-connection/utils"); + require('guacamole-common-js') + var ConnectionTypes = require('utils/guacamole/types/connection-types'); + var ManagedClient = require('utils/guacamole/types/client'); + var ManagedClientState = require('utils/guacamole/types/client-state'); + var Utils = require('utils/guacamole/utils'); + var UtilsConnection = require('utils/info-connection/utils'); - var GuacButtons = require("utils/guacamole/directives/guacButtons"); - var GuacClipboard = require("utils/guacamole/directives/guacClipboard"); - var GuacKeyboard = require("utils/guacamole/directives/guacKeyboard"); - var GuacMouse = require("utils/guacamole/directives/guacMouse"); - var GuacOsk = require("utils/guacamole/directives/guacOsk"); + var GuacButtons = require('utils/guacamole/directives/guacButtons'); + var GuacClipboard = require('utils/guacamole/directives/guacClipboard'); + var GuacKeyboard = require('utils/guacamole/directives/guacKeyboard'); + var GuacMouse = require('utils/guacamole/directives/guacMouse'); + var GuacOsk = require('utils/guacamole/directives/guacOsk'); function GuacController() { var $guac = {}; var $scope = {}; var $elements = { - main: document.getElementById('guacamole-main'), - displayContainer: document.getElementById('guacamole-display'), + main: document.querySelector('.wrapper__display'), + displayContainer: document.getElementById('display'), osk: document.getElementById('osk'), - closeOskButton: document.getElementById('osk-close'), + closeOskButton: document.querySelector('.osk__header__buttons .close'), /* Buttons */ - sendCtrlAltDelButton: document.getElementById('sendCtrlAltDelButton'), - mouseButton: document.getElementById('mouseButton'), - screenshotButton: document.getElementById('takeScreenshot'), - oskButton: document.getElementById('oskButton'), + sendCtrlAltDelButton: document.getElementById('buttons__sendctrlaltdel'), + mouseButton: document.getElementById('buttons__mouse'), + screenshotButton: document.getElementById('buttons__screenshot'), + oskButton: document.getElementById('buttons__osk'), + fullscreenButton: document.getElementById('buttons__fullscreen'), }; - var throttleResizeFunction = Utils.throttle(containerResized, 250); - window.addEventListener('resize', throttleResizeFunction); + this.disconnect = disconnect; - this.disconnect = function() { - if ($guac.client) $guac.client.disconnect(); - if ($guac.keyboard) GuacKeyboard.destroy(); - if ($guac.mouse) GuacMouse.destroy(); - if ($guac.osk) GuacOsk.destroy(); + this.setConnection = function(token, connectionType) { + $scope.connectionType = String(connectionType).toUpperCase(); - GuacButtons.destroy(); - GuacClipboard.destroy(); - window.removeEventListener('resize', throttleResizeFunction); - $('#guacamole-state').text(''); + $scope.connectionType === ConnectionTypes.SSH + ? $elements.displayContainer.classList.add('ssh') + : $elements.displayContainer.classList.remove('ssh'); - $guac = {}; - $scope = {}; - } - - this.setConnection = function(token) { var managedClient = ManagedClient.getInstance(token, undefined, $elements.displayContainer) new GuacKeyboard($guac, $scope, $elements); @@ -70,14 +62,16 @@ define(function(require) { new GuacOsk($guac, $scope, $elements); new GuacButtons($guac, $scope, $elements); new GuacClipboard($guac, $scope, $elements); + window.addEventListener('resize', containerResized); + document.addEventListener('fullscreenchange', containerResized) // Remove any existing display - $elements.displayContainer.innerHTML = ""; + $elements.displayContainer.innerHTML = ''; // Only proceed if a client is given if (!managedClient) return; $scope.client = managedClient; - + // Get Guacamole client instance $guac.client = managedClient.client; @@ -108,36 +102,40 @@ define(function(require) { } }).bind(this)); - Utils.observe($scope, 'disableCursor', (function(disabled) { - $elements.mouseButton.disabled = !!disabled; - }).bind(this)); - Utils.observe($scope.client.clientState, 'connectionState', (function(connectionState) { var isLoading = connectionState === ManagedClientState.ConnectionState.WAITING; - - $('#guacamole-loading')[isLoading ? 'fadeIn' : 'fadeOut']('fast'); - $('#guacamole-state').text(connectionState).animate(); - }).bind(this)); + var isConnected = connectionState === ManagedClientState.ConnectionState.CONNECTED; + var isDisconnected = connectionState === ManagedClientState.ConnectionState.DISCONNECTED; - Utils.observe($scope.client.clientProperties, 'scale', (function(scale) { - scale = Math.max(scale, $scope.client.clientProperties.minScale); - scale = Math.min(scale, $scope.client.clientProperties.maxScale); - - // Apply scale if client attached - if ($guac.display && scale !== 0) { - $guac.display.scale(scale); - $elements.displayContainer.style['min-height'] = $guac.display.getHeight() + "px"; - } + isConnected && setTimeout(containerResized, 100); + isDisconnected && disconnect(); - if (scale !== $scope.client.clientProperties.scale) { - $scope.client.clientProperties.scale = scale; - } + $('.spinner')[isLoading ? 'fadeIn' : 'fadeOut']('fast'); + $('.toolbar__state h5').text(connectionState).animate(); }).bind(this)); }; this.setInformation = function(information) { var info_decode = UtilsConnection.decodeInfoConnection(information); - UtilsConnection.printInfoConnection($('.guacamole_info'), info_decode); + UtilsConnection.printInfoConnection($('.information'), info_decode); + } + + function disconnect() { + if ($guac.client) $guac.client.disconnect(); + if ($guac.keyboard) GuacKeyboard.destroy(); + if ($guac.mouse) GuacMouse.destroy(); + if ($guac.osk) GuacOsk.destroy(); + + GuacButtons.destroy(); + GuacClipboard.destroy(); + window.removeEventListener('resize', containerResized); + document.removeEventListener('fullscreenchange', containerResized) + + while($elements.displayContainer.firstChild) + $elements.displayContainer.removeChild($elements.displayContainer.firstChild); + + $guac = {}; + $scope = {}; } function containerResized() { @@ -150,38 +148,37 @@ define(function(require) { if ($guac.display.getWidth() !== width || $guac.display.getHeight() !== height) { $guac.client.sendSize(width, height); } - - if ($guac.osk) { - var MAX_OSK_WIDTH = 1000; - $guac.osk.resize(Math.min(MAX_OSK_WIDTH, width)); - } + // when type connection is SSH, display doesn't need scale + $scope.connectionType !== ConnectionTypes.SSH && updateDisplayScale(); } - updateDisplayScale(); + if ($guac.osk) { + $guac.osk.resize(1000); + } }; function updateDisplayScale() { if (!$guac.display) return; - // Calculate scale to fit screen - $scope.client.clientProperties.minScale = Math.min( - $elements.main.offsetWidth / Math.max($guac.display.getWidth(), 1), - $elements.main.offsetHeight / Math.max($guac.display.getHeight(), 1) - ); + // Get screen resolution. + var origHeight = Math.max($guac.display.getHeight(), 1); + var origWidth = Math.max($guac.display.getWidth(), 1); + + var htmlWidth = window.innerWidth; + var htmlHeight = window.innerHeight; + + var xScale = htmlWidth / origWidth; + var yScale = htmlHeight / origHeight; + + // This is done to handle both X and Y axis + var scale = Math.min(yScale, xScale); - // Calculate appropriate maximum zoom level - $scope.client.clientProperties.maxScale = Math.max($scope.client.clientProperties.minScale, 3); + // Limit to 1 + scale = Math.min(scale, 1); - // Clamp zoom level, maintain auto-fit - if ( - $guac.display.getScale() < $scope.client.clientProperties.minScale || - $scope.client.clientProperties.autoFit - ) { - $scope.client.clientProperties.scale = $scope.client.clientProperties.minScale; - } - else if ($guac.display.getScale() > $scope.client.clientProperties.maxScale) { - $scope.client.clientProperties.scale = $scope.client.clientProperties.maxScale; + if (scale !== 0) { + $guac.display.scale(scale); } }; } diff --git a/src/sunstone/public/app/utils/guacamole/directives/guacButtons.js b/src/sunstone/public/app/utils/guacamole/directives/guacButtons.js index 38ce4e5016..5c7539c41f 100644 --- a/src/sunstone/public/app/utils/guacamole/directives/guacButtons.js +++ b/src/sunstone/public/app/utils/guacamole/directives/guacButtons.js @@ -17,6 +17,7 @@ define(function(require) { var Files = require('utils/files'); + var ConnectionTypes = require("utils/guacamole/types/connection-types"); function GuacButtons($guac, $scope, $elements) { $elements.screenshotButton.onclick = function() { @@ -45,7 +46,7 @@ define(function(require) { $elements.closeOskButton.onclick = function() { if (!$guac.client) return; - $('#osk-container').fadeToggle('fast'); + $('#osk__container').fadeToggle('fast'); }; $elements.mouseButton.onclick = function() { @@ -55,16 +56,25 @@ define(function(require) { $scope.localCursor = $elements.mouseButton.classList.contains('disabled'); }; + $elements.fullscreenButton.onclick = function() { + // If the document is not in full screen mode make the video full screen + if (!document.fullscreenElement && document.fullscreenEnabled) { + $elements.main.requestFullscreen(); + } else if (document.exitFullscreen) { + document.exitFullscreen(); + } + }; + GuacButtons.destroy = function() { // reset default state - $('#osk-container').hide(); + $('#osk__container').hide(); $elements.mouseButton.classList.remove('disabled'); - $elements.sendCtrlAltDelButton = - $elements.screenshotButton.onclick = - $elements.mouseButton.onclick = - $elements.oskButton.onclick = - $elements.closeOskButton.onclick = null; + $elements.sendCtrlAltDelButton.onclick = + $elements.screenshotButton.onclick = + $elements.mouseButton.onclick = + $elements.oskButton.onclick = + $elements.closeOskButton.onclick = null; }; } diff --git a/src/sunstone/public/app/utils/guacamole/directives/guacMouse.js b/src/sunstone/public/app/utils/guacamole/directives/guacMouse.js index ee3c24fad2..d80e483953 100644 --- a/src/sunstone/public/app/utils/guacamole/directives/guacMouse.js +++ b/src/sunstone/public/app/utils/guacamole/directives/guacMouse.js @@ -30,9 +30,7 @@ define(function(require) { // Forward mousemove events untouched mouse.onmousemove = function(mouseState) { - mouseState.y = mouseState.y / $guac.display.getScale(); - mouseState.x = mouseState.x / $guac.display.getScale(); - handleMouseState(mouseState); + handleMouseState(mouseState, true); } // Hide software cursor when mouse leaves display @@ -42,10 +40,15 @@ define(function(require) { $guac.display.showCursor(false); }; - function handleMouseState(mouseState) { + function handleMouseState(mouseState, scaleMouse = false) { // Do not attempt to handle mouse state changes if the client // or display are not yet available - if (!$guac.client || !$guac.display) return; + if (!$guac.client || !$guac.display || $scope.disabledMouse) return; + + if (scaleMouse) { + mouseState.y = mouseState.y / $guac.display.getScale(); + mouseState.x = mouseState.x / $guac.display.getScale(); + } // Send mouse state, show cursor if necessary $guac.display.showCursor(!$scope.localCursor); diff --git a/src/sunstone/public/app/utils/guacamole/directives/guacOsk.js b/src/sunstone/public/app/utils/guacamole/directives/guacOsk.js index ece5a457db..e3b4a61018 100644 --- a/src/sunstone/public/app/utils/guacamole/directives/guacOsk.js +++ b/src/sunstone/public/app/utils/guacamole/directives/guacOsk.js @@ -26,18 +26,21 @@ define(function(require) { loadLayouts(); changeLayout(DEFAULT_LAYOUT); - // $('#osk-container').draggable(); + $('#osk__container').draggable({ + start: function() { $scope.disabledMouse = true; }, + stop: function() { $scope.disabledMouse = false; } + }); function loadLayouts() { - $('#osk-qwerty').empty(); + $('#select__qwerty').empty(); var enUsLayout = new Option(enUsQwerty.language, enUsQwerty.language); - $('#osk-qwerty').append(enUsLayout); + $('#select__qwerty').append(enUsLayout); var esEsLayout = new Option(esEsQwerty.language, esEsQwerty.language); - $('#osk-qwerty').append(esEsLayout); + $('#select__qwerty').append(esEsLayout); - $('#osk-qwerty').off().on('change', function() { + $('#select__qwerty').off().on('change', function() { changeLayout(this.value); }) }; diff --git a/src/sunstone/public/app/utils/guacamole/types/client.js b/src/sunstone/public/app/utils/guacamole/types/client.js index c7adc7ee0c..bcf7654a3d 100644 --- a/src/sunstone/public/app/utils/guacamole/types/client.js +++ b/src/sunstone/public/app/utils/guacamole/types/client.js @@ -215,9 +215,15 @@ define(function(require) { function getConnectString(token, connectionParameters, display = window) { // Calculate optimal width/height for display var pixel_density = window.devicePixelRatio || 1; - var optimal_width = display.innerWidth * pixel_density; - var optimal_height = display.innerHeight * pixel_density; var optimal_dpi = pixel_density * 96; + + var optimal_width = display instanceof Window + ? display.innerWidth * pixel_density + : display.offsetWidth * pixel_density; + + var optimal_height = display instanceof Window + ? display.innerHeight * pixel_density + : display.offsetHeight * pixel_density; // Build base connect string var connectString = [ diff --git a/src/sunstone/public/app/utils/guacamole/types/connection-types.js b/src/sunstone/public/app/utils/guacamole/types/connection-types.js new file mode 100644 index 0000000000..543316b8f2 --- /dev/null +++ b/src/sunstone/public/app/utils/guacamole/types/connection-types.js @@ -0,0 +1,31 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +define(function() { + + /** + * Valid connection type strings. Each type string represents a protocol. + */ + var ConnectionTypes = { + SSH : "SSH", + RDP : "RDP", + VNC : "VNC", + TELNET : "TELNET" + } + + return ConnectionTypes; + +}); \ No newline at end of file diff --git a/src/sunstone/public/css/guac-custom.css b/src/sunstone/public/css/guac-custom.css index 49ee565036..170049502e 100644 --- a/src/sunstone/public/css/guac-custom.css +++ b/src/sunstone/public/css/guac-custom.css @@ -1,87 +1,122 @@ -body { - margin:0; - padding:0; - font-family: Helvetica; - height:100%; -} +/* +* CSS TABLE OF CONTENTS +* +* 1.0 - Globals +* 2.0 - Header +* 3.0 - Body +* 3.1 - Guacamole display +* 3.2 - Defaults classes by guacamole-common-js +*/ + +/*** 1.0 - Globals ***/ html { - height:100%; + box-sizing: border-box; } -.remote-buttons { - white-space: nowrap; +*, *:before, *:after { + box-sizing: inherit; } -.main { - width: 100%; - display: inline-flex; - height: 100%; - flex-direction: column; - background-color: rgb(40, 40, 40); +body { + display: grid; + grid-template-rows: auto 1fr; } -.guacamole-main{ - flex-grow: 1; +h5 { + margin: 0; + padding: 0; } -.guacamole-status { - text-align: center; - position: relative; - width: 100%; +/*** 2.0 - Header ***/ + +header { + background: #f2f4f8; + padding: 1em 1em 0.8em; + box-shadow: 0px 2px 1px -1px rgb(0 0 0 / 20%), + 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 1px 3px 0px rgb(0 0 0 / 12%); } -.remote-logo{ - border-radius: 50%; - background-color: #282828; - padding: 0.4em; - margin-right: 1em; - height: 40px; - width: 40px; +.header__wrapper { + max-width: 1250px; + margin: 0 auto; } -.container{ +.toolbar { + padding: 1em; display: flex; - justify-content: center; + gap: 2em; + flex-wrap: wrap; +} + +.toolbar img { + height: 40px; +} + +.toolbar__state { + flex-grow: 1; + display: flex; + gap: 1em; align-items: center; + justify-content: center; } -.guacamole-state{ - white-space: nowrap; - max-width: 20em; - overflow: hidden; - text-overflow: ellipsis; +.toolbar__state img { + width: 40px; + background-color: #282828; + border-radius: 50%; + padding: 0.4em; } +.toolbar__buttons { + display: flex; + justify-content: flex-end; +} -/* left-align the status text on lower resolutions */ -@media screen and (max-width: 800px){ - .guacamole-status { - z-index: 1; - position: relative; - width: auto; - float: left; +@media (max-width: 768px) { + .toolbar__state { + justify-content: end; + flex-direction: row-reverse; + } + .toolbar__buttons { + flex-grow: 1; } } -.guacamole-main { - width: 100%; - height: fit-content; - display: flex; - align-items: center; - place-content: center; - background-color: #282828; +/*** 3.0 - Body ***/ + +main { + background-color: #222431; } -.guacamole-main > div { +main > div { z-index: 1; } -.guacamole-main .guacamole-display { +/*** 3.1 - Guacamole display ***/ + +.wrapper__display { + width: 100%; + height: 100%; + + display: flex; + align-items: center; + place-content: center; +} + +#display { + overflow: hidden; cursor: none; } -.osk-container { +#display.ssh { + width: 100vw; + height: 100%; +} + +/*** 3.2 - Guacamole OSK ***/ + +.osk__container { z-index: 2; background: rgba(0, 0, 0, 0.59); position: absolute; @@ -93,30 +128,24 @@ html { box-shadow: 0 0 20px #acacac; } -.osk-container-header { +.osk__header { background: linear-gradient(to top, #ebebeb, #d5d5d5); color: #4d494d; - font-size: 11pt; - line-height: 20px; - text-align: center; - width: 100%; - height: 28px; + user-select: none; cursor: default; + border-top: 1px solid #f3f1f3; - border-bottom: 1px solid #b1aeb1; border-top-left-radius: 6px; border-top-right-radius: 6px; + + padding: 0.5em; + display: flex; + justify-content: space-between; + align-items: start; } -.osk-container-header .buttons { - padding-left: 8px; - padding-top: 3px; - float: left; - line-height: 0; -} - -.osk-container-header .buttons .close { +.osk__header__buttons .close { background: #ff5c5c; font-size: 13px; font-weight: bold; @@ -126,18 +155,17 @@ html { display: inline-block; } -.osk-container-header .layouts { - padding-right: 8px; - padding-top: 3px; - float: right; - line-height: 0; +.osk__header select { + width: auto; + margin: 0; } +/*** 3.2 Defaults classes by guacamole-common-js ***/ .guac-keyboard { display: inline-block; width: 100%; - + margin: 0; padding: 0; cursor: default; @@ -159,20 +187,19 @@ html { } .guac-keyboard .guac-keyboard-key { - position: absolute; - left: 0; - right: 0; - top: 0; + left: 0; + right: 0; + top: 0; bottom: 0; background: #444; border: 0.125em solid #666; - -moz-border-radius: 0.25em; + -moz-border-radius: 0.25em; -webkit-border-radius: 0.25em; - -khtml-border-radius: 0.25em; - border-radius: 0.25em; + -khtml-border-radius: 0.25em; + border-radius: 0.25em; color: white; font-size: 40%; @@ -180,11 +207,8 @@ html { text-align: center; white-space: pre; - text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25), - 1px -1px 0 rgba(0, 0, 0, 0.25), - -1px 1px 0 rgba(0, 0, 0, 0.25), - -1px -1px 0 rgba(0, 0, 0, 0.25); - + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25), 1px -1px 0 rgba(0, 0, 0, 0.25), + -1px 1px 0 rgba(0, 0, 0, 0.25), -1px -1px 0 rgba(0, 0, 0, 0.25); } .guac-keyboard .guac-keyboard-key:hover { @@ -232,12 +256,12 @@ html { /* Active super */ .guac-keyboard.guac-keyboard-modifier-super .guac-keyboard-key-super { background: #882; - border-color: #DD4; + border-color: #dd4; } .guac-keyboard .guac-keyboard-key.guac-keyboard-pressed { background: #822; - border-color: #D44; + border-color: #d44; } .guac-keyboard .guac-keyboard-group { @@ -252,7 +276,6 @@ html { } .guac-keyboard .guac-keyboard-group.guac-keyboard-main { - /* IE10 */ display: -ms-flexbox; -ms-flex-align: stretch; @@ -262,7 +285,7 @@ html { display: -moz-box; -moz-box-align: stretch; -moz-box-orient: horizontal; - + /* Ancient WebKit */ display: -webkit-box; -webkit-box-align: stretch; @@ -277,7 +300,6 @@ html { display: flex; align-items: stretch; flex-direction: row; - } .guac-keyboard .guac-keyboard-group.guac-keyboard-movement { @@ -315,13 +337,11 @@ html { .guac-keyboard.guac-keyboard-modifier-alt-gr .guac-keyboard-key.guac-keyboard-uses-alt-gr .guac-keyboard-cap:not(.guac-keyboard-requires-alt-gr) { - display: none; - } /* Fade out keys which do not use AltGr if AltGr is active */ .guac-keyboard.guac-keyboard-modifier-alt-gr -.guac-keyboard-key:not(.guac-keyboard-uses-alt-gr):not(.guac-keyboard-key-alt-gr) { + .guac-keyboard-key:not(.guac-keyboard-uses-alt-gr):not(.guac-keyboard-key-alt-gr) { opacity: 0.5; -} \ No newline at end of file +} diff --git a/src/sunstone/public/scss/_settings.scss b/src/sunstone/public/scss/_settings.scss index 5920359272..9e360fdbf1 100644 --- a/src/sunstone/public/scss/_settings.scss +++ b/src/sunstone/public/scss/_settings.scss @@ -49,7 +49,6 @@ $global-font-size: 13px; $global-width: rem-calc(1200); $global-lineheight: 1.5; $foundation-palette: ( - //primary: #2199e8, primary: #4DBBD3, secondary: #8a8a8a, success: #3adb76, diff --git a/src/sunstone/views/guac.erb b/src/sunstone/views/guac.erb index 75d369a8ef..5a57262eb0 100644 --- a/src/sunstone/views/guac.erb +++ b/src/sunstone/views/guac.erb @@ -4,66 +4,64 @@ - - - - + -
-
-
-
- -
-
- -
- - - -
-
-
-
- - - - -
+
+
+
+ +
+ +
+ + + +
+
+ + + + + +
-
+
-
-
+
+
+ <%### Guacamole display screen ###%> +
+
- -
-
-
- -
-
- + + <%### Guacamole on-screen keyboard ###%> +
+
+
+
+
-
+
<% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %> diff --git a/src/sunstone/views/spice.erb b/src/sunstone/views/spice.erb index 60f0f1bb1e..54fdf8dc04 100644 --- a/src/sunstone/views/spice.erb +++ b/src/sunstone/views/spice.erb @@ -37,7 +37,7 @@
- +
diff --git a/src/sunstone/views/vmrc.erb b/src/sunstone/views/vmrc.erb index 3559f4e1fb..664bfa365f 100644 --- a/src/sunstone/views/vmrc.erb +++ b/src/sunstone/views/vmrc.erb @@ -72,7 +72,7 @@
- +
diff --git a/src/sunstone/views/vnc.erb b/src/sunstone/views/vnc.erb index eaf0023222..0830a476bd 100644 --- a/src/sunstone/views/vnc.erb +++ b/src/sunstone/views/vnc.erb @@ -17,7 +17,7 @@
- +