KASM-5411 Use windows placement api (#86)
Automatic placement of new displays using the Windows API if available. Control panel for secondary displays.
This commit is contained in:
parent
246dbd4999
commit
4dac080460
|
@ -1371,6 +1371,12 @@ a:visited {
|
|||
#noVNC_setting_enable_hidpi_option.show {
|
||||
display: flex!important;
|
||||
}
|
||||
#noVNC_auto_placement_option {
|
||||
display: none!important;
|
||||
}
|
||||
#noVNC_auto_placement_option.show {
|
||||
display: flex!important;
|
||||
}
|
||||
#noVNC_refreshMonitors {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
:root {
|
||||
--text-color: rgb(147, 166, 188);
|
||||
--bg: rgb(33 39 63 / 0.98);
|
||||
--gray-500: 107 114 128;
|
||||
}
|
||||
.light {
|
||||
--text-color: rgb(22, 45, 72);
|
||||
--bg: rgba(234, 235, 240, 0.98);
|
||||
--gray-500: 209 213 219;
|
||||
}
|
||||
#mySidenav {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
transition: 0.5s;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: translateX(calc(-100% - 30px));
|
||||
color: var(--text-color);
|
||||
}
|
||||
#mySidenav * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#mySidenav button {
|
||||
border: 0;
|
||||
color: var(--text-color);
|
||||
}
|
||||
.sidenavnew {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background-color: var(--bg);
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 30px;
|
||||
flex-direction: column;
|
||||
}
|
||||
#mySidenav .sidebar-icons-list .sidebar-icons {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
gap: 12px;
|
||||
text-align: initial;
|
||||
}
|
||||
#mySidenav.loadin {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
#mySidenav .innertab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px 5px;
|
||||
}
|
||||
#mySidenav.loadin.show_nav {
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
#mySidenav .tab {
|
||||
position: absolute;
|
||||
right: -30px;
|
||||
top: calc(50% - 30px);
|
||||
white-space: nowrap;
|
||||
}
|
||||
#menuTab.dragging {
|
||||
padding-top: 5rem/* 80px */;
|
||||
padding-bottom: 5rem/* 80px */;
|
||||
margin-top: -5rem/* -80px */;
|
||||
padding-right: 2rem/* 32px */;
|
||||
margin-right: -2rem/* -32px */;
|
||||
}
|
||||
.sidebar-icon-container {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
flex: 0 0 36px;
|
||||
}
|
||||
.text-white {
|
||||
--text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--text-opacity));
|
||||
}
|
||||
.bg-gray-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(var(--gray-500) / var(--tw-bg-opacity));
|
||||
}
|
||||
svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa {
|
||||
overflow: visible;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
|
||||
.fa-lg {
|
||||
font-size: 1.25em;
|
||||
line-height: 0.05em;
|
||||
vertical-align: -0.075em;
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
}
|
||||
.bg-emerald-500 {
|
||||
--bg-opacity: 1;
|
||||
background-color: rgb(16 185 129 / var(--bg-opacity));
|
||||
}
|
||||
.bg-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
.touch-none {
|
||||
touch-action: none;
|
||||
}
|
||||
.rotate-180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
#mySidenav .tabinner {
|
||||
background: var(--bg);
|
||||
width: 30px;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.rounded-r-lg {
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
}
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.py-1 {
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.bg-black\/5 {
|
||||
background-color: rgb(0 0 0 / 0.05);
|
||||
}
|
||||
.rounded-tr-lg {
|
||||
border-top-right-radius: 0.5rem;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.cursor-move {
|
||||
cursor: move;
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.py-5 {
|
||||
padding-top: 1.25rem;
|
||||
padding-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
.text-xs {
|
||||
font-size: 0.75rem/* 12px */;
|
||||
line-height: 1rem/* 16px */;
|
||||
}
|
84
app/ui.js
84
app/ui.js
|
@ -66,6 +66,7 @@ const UI = {
|
|||
sortedMonitors: [],
|
||||
selectedMonitor: null,
|
||||
refreshRotation: 0,
|
||||
currentDisplay: null,
|
||||
|
||||
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
||||
|
||||
|
@ -196,6 +197,15 @@ const UI = {
|
|||
UI.addOption(document.getElementById('noVNC_setting_logging'), llevels[i], llevels[i]);
|
||||
}
|
||||
|
||||
if ('getScreenDetails' in window) {
|
||||
document.getElementById('noVNC_auto_placement_option').classList.add("show");
|
||||
}
|
||||
|
||||
const initialAutoPlacementValue = window.localStorage.getItem('autoPlacement')
|
||||
if (initialAutoPlacementValue === null) {
|
||||
document.getElementById("noVNC_auto_placement").checked = true
|
||||
}
|
||||
|
||||
// Settings with immediate effects
|
||||
UI.initSetting('logging', 'warn');
|
||||
UI.updateLogging();
|
||||
|
@ -507,6 +517,7 @@ const UI = {
|
|||
UI.addClickHandle('noVNC_settings_button', UI.toggleSettingsPanel);
|
||||
|
||||
document.getElementById("noVNC_setting_enable_perf_stats").addEventListener('click', UI.showStats);
|
||||
document.getElementById("noVNC_auto_placement").addEventListener('change', UI.setAutoPlacement);
|
||||
|
||||
UI.addSettingChangeHandler('encrypt');
|
||||
UI.addSettingChangeHandler('resize');
|
||||
|
@ -597,6 +608,14 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
setAutoPlacement(e) {
|
||||
if (e.target.checked === false) {
|
||||
window.localStorage.setItem('autoPlacement', false)
|
||||
} else {
|
||||
window.localStorage.removeItem('autoPlacement')
|
||||
}
|
||||
},
|
||||
|
||||
/*addMultiMonitorAddHandler() {
|
||||
if (UI.supportsBroadcastChannel) {
|
||||
UI.addClickHandle('noVNC_addmonitor_button', UI.addSecondaryMonitor);
|
||||
|
@ -1891,10 +1910,53 @@ const UI = {
|
|||
UI.draw()
|
||||
},
|
||||
|
||||
addSecondaryMonitor() {
|
||||
normalizePlacementValues(details) {
|
||||
|
||||
},
|
||||
|
||||
increaseCurrentDisplay(details) {
|
||||
const max = details.screens.length
|
||||
const thisIndex = details.screens.findIndex(el => el === details.currentScreen)
|
||||
if (max === 1) {
|
||||
return 0
|
||||
}
|
||||
if (UI.currentDisplay === null) {
|
||||
UI.currentDisplay = thisIndex
|
||||
}
|
||||
UI.currentDisplay += 1
|
||||
if (UI.currentDisplay === thisIndex) {
|
||||
UI.currentDisplay += 1
|
||||
}
|
||||
if (UI.currentDisplay >= max) {
|
||||
UI.currentDisplay = 0
|
||||
}
|
||||
return UI.currentDisplay
|
||||
},
|
||||
|
||||
async addSecondaryMonitor() {
|
||||
let new_display_path = window.location.pathname.replace(/[^/]*$/, '')
|
||||
let new_display_url = `${window.location.protocol}//${window.location.host}${new_display_path}screen.html`;
|
||||
|
||||
const auto_placement = document.getElementById('noVNC_auto_placement').checked
|
||||
if (auto_placement && 'getScreenDetails' in window) {
|
||||
let permission = false;
|
||||
try {
|
||||
const { state } = await navigator.permissions.query({ name: 'window-management' });
|
||||
permission = (state === 'granted' || state === 'prompt');
|
||||
if (permission && window.screen.isExtended) {
|
||||
const details = await window.getScreenDetails()
|
||||
const current = UI.increaseCurrentDisplay(details)
|
||||
let screen = details.screens[current]
|
||||
const options = 'left='+screen.availLeft+',top='+screen.availTop+',width='+screen.availWidth+',height='+screen.availHeight+',fullscreen'
|
||||
window.open(new_display_url, '_blank', options);
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
// Nothing.
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug(`Opening a secondary display ${new_display_url}`)
|
||||
window.open(new_display_url, '_blank', 'toolbar=0,location=0,menubar=0');
|
||||
},
|
||||
|
@ -1997,7 +2059,12 @@ const UI = {
|
|||
|
||||
rect(ctx, x, y, w, h) {
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(x, y, w, h, 5);
|
||||
if (typeof ctx.roundRect !== 'undefined') {
|
||||
ctx.roundRect(x, y, w, h, 5);
|
||||
} else {
|
||||
// fallback for old browsers
|
||||
ctx.rect(x, y, w, h);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
@ -2066,7 +2133,6 @@ const UI = {
|
|||
for (var i = 0; i < monitors.length; i++) {
|
||||
var monitor = monitors[i];
|
||||
var a = sortedMonitors.find(el => el.id === monitor.id)
|
||||
console.log(a)
|
||||
screens.push({
|
||||
screenID: a.id,
|
||||
serverHeight: Math.round(a.h * scale),
|
||||
|
@ -2080,8 +2146,6 @@ const UI = {
|
|||
serverWidth: Math.round(width * scale),
|
||||
screens
|
||||
}
|
||||
console.log('setScreenPlan')
|
||||
console.log(screenPlan)
|
||||
UI.rfb.applyScreenPlan(screenPlan);
|
||||
},
|
||||
|
||||
|
@ -2839,11 +2903,19 @@ const UI = {
|
|||
|
||||
screenRegistered(e) {
|
||||
console.log('screen registered')
|
||||
|
||||
// Get the current screen plan
|
||||
// When a new display is added, it is defaulted to be placed to the far right relative to existing displays and to the top
|
||||
if (UI.rfb) {
|
||||
let screenPlan = UI.rfb.getScreenPlan();
|
||||
console.log(screenPlan)
|
||||
if (e && e.detail) {
|
||||
const { left, top, screenID } = e.detail
|
||||
const current = screenPlan.screens.findIndex(el => el.screenID === screenID)
|
||||
if (current) {
|
||||
screenPlan.screens[current].x = left
|
||||
screenPlan.screens[current].y = top
|
||||
}
|
||||
}
|
||||
|
||||
UI.updateMonitors(screenPlan)
|
||||
UI._identify(UI.monitors)
|
||||
|
|
106
app/ui_screen.js
106
app/ui_screen.js
|
@ -12,15 +12,14 @@ const UI = {
|
|||
screens: [],
|
||||
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
||||
controlChannel: null,
|
||||
draggingTab: false,
|
||||
//Initial Loading of the UI
|
||||
prime() {
|
||||
console.log('prime')
|
||||
this.start();
|
||||
},
|
||||
|
||||
//Render default UI
|
||||
start() {
|
||||
console.log('start')
|
||||
window.addEventListener("unload", (e) => {
|
||||
if (UI.rfb) {
|
||||
UI.disconnect();
|
||||
|
@ -33,10 +32,88 @@ const UI = {
|
|||
|
||||
UI.addDefaultHandlers();
|
||||
UI.updateVisualState('disconnected');
|
||||
|
||||
const webui_mode = window.localStorage.getItem('theme')?.toLowerCase() || 'dark'
|
||||
document.getElementById('screen').classList.add(webui_mode);
|
||||
|
||||
},
|
||||
|
||||
addDefaultHandlers() {
|
||||
document.getElementById('noVNC_connect_button').addEventListener('click', UI.connect);
|
||||
// Control panel events
|
||||
document.getElementById('toggleMenu').addEventListener('click', UI.toggleMenu);
|
||||
document.getElementById('closeMenu').addEventListener('click', UI.toggleMenu);
|
||||
document.getElementById('fullscreenTrigger').addEventListener('click', UI.fullscreenTrigger);
|
||||
document.getElementById('menuTab').addEventListener('mousemove', UI.dragTab);
|
||||
document.getElementById('menuTab').addEventListener('mouseup', UI.dragEnd);
|
||||
document.getElementById('menuTab').addEventListener('touchmove', UI.touchDragTab);
|
||||
document.getElementById('dragHandler').addEventListener('mousedown', UI.dragStart);
|
||||
document.getElementById('dragHandler').addEventListener('touchstart', UI.dragStart);
|
||||
document.getElementById('dragHandler').addEventListener('mouseup', UI.dragEnd);
|
||||
document.getElementById('dragHandler').addEventListener('touchend', UI.dragEnd);
|
||||
document.getElementById('menuTab').addEventListener('mouseleave', UI.dragEnd);
|
||||
// End control panel events
|
||||
},
|
||||
|
||||
dragStart(e) {
|
||||
UI.draggingTab = true
|
||||
},
|
||||
dragEnd(e) {
|
||||
document.getElementById('menuTab').classList.remove('dragging')
|
||||
UI.draggingTab = false
|
||||
},
|
||||
|
||||
dragTab(e) {
|
||||
if (UI.draggingTab) {
|
||||
document.getElementById('menuTab').style.top = (e.clientY - 10) + 'px'
|
||||
document.getElementById('menuTab').classList.add('dragging')
|
||||
}
|
||||
},
|
||||
touchDragTab(e) {
|
||||
if (UI.draggingTab) {
|
||||
e.preventDefault()
|
||||
const touch = e.touches[0]
|
||||
document.getElementById('menuTab').style.top = (touch.clientY - 10) + 'px'
|
||||
document.getElementById('menuTab').classList.add('dragging')
|
||||
}
|
||||
},
|
||||
|
||||
fullscreenTrigger() {
|
||||
if (document.fullscreenElement || // alternative standard method
|
||||
document.mozFullScreenElement || // currently working methods
|
||||
document.webkitFullscreenElement ||
|
||||
document.msFullscreenElement) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
} else {
|
||||
if (document.documentElement.requestFullscreen) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else if (document.documentElement.mozRequestFullScreen) {
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
} else if (document.documentElement.webkitRequestFullscreen) {
|
||||
document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else if (document.body.msRequestFullscreen) {
|
||||
document.body.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
toggleMenu() {
|
||||
document.getElementById('mySidenav').classList.toggle('show_nav')
|
||||
const show = document.getElementById('mySidenav').classList.contains('show_nav')
|
||||
if (show) {
|
||||
document.getElementById('toggleMenuIcon').classList.add('rotate-180')
|
||||
} else {
|
||||
document.getElementById('toggleMenuIcon').classList.remove('rotate-180')
|
||||
}
|
||||
},
|
||||
|
||||
getSetting(name, isBool, default_value) {
|
||||
|
@ -55,7 +132,16 @@ const UI = {
|
|||
},
|
||||
|
||||
connect() {
|
||||
console.log('connect')
|
||||
|
||||
let details = null
|
||||
const initialAutoPlacementValue = window.localStorage.getItem('autoPlacement')
|
||||
if (initialAutoPlacementValue === null) {
|
||||
details = {
|
||||
left: window.screenLeft,
|
||||
top: window.screenTop
|
||||
}
|
||||
}
|
||||
|
||||
UI.rfb = new RFB(document.getElementById('noVNC_container'),
|
||||
document.getElementById('noVNC_keyboardinput'),
|
||||
"", //URL
|
||||
|
@ -103,20 +189,17 @@ const UI = {
|
|||
}
|
||||
|
||||
if (UI.supportsBroadcastChannel) {
|
||||
console.log('add event listener')
|
||||
UI.controlChannel = new BroadcastChannel(UI.rfb.connectionID);
|
||||
UI.controlChannel.addEventListener('message', UI.handleControlMessage)
|
||||
}
|
||||
|
||||
//attach this secondary display to the primary display
|
||||
if (UI.screenID === null) {
|
||||
const screen = UI.rfb.attachSecondaryDisplay();
|
||||
const screen = UI.rfb.attachSecondaryDisplay(details);
|
||||
UI.screenID = screen.screenID
|
||||
UI.screen = screen
|
||||
} else {
|
||||
console.log('else reattach screens')
|
||||
console.log(UI.screen)
|
||||
UI.rfb.reattachSecondaryDisplay(UI.screen);
|
||||
UI.rfb.reattachSecondaryDisplay(UI.screen, details);
|
||||
}
|
||||
document.querySelector('title').textContent = 'Display ' + UI.screenID
|
||||
|
||||
|
@ -171,7 +254,6 @@ const UI = {
|
|||
document.documentElement.classList.add("noVNC_disconnecting");
|
||||
break;
|
||||
case 'disconnected':
|
||||
console.log('disconnected')
|
||||
document.documentElement.classList.add("noVNC_disconnected");
|
||||
if (connect_el.classList.contains("noVNC_hidden")) {
|
||||
connect_el.classList.remove('noVNC_hidden');
|
||||
|
@ -191,7 +273,6 @@ const UI = {
|
|||
|
||||
identify(data) {
|
||||
UI.screens = data.screens
|
||||
console.log('identify')
|
||||
const screen = data.screens.find(el => el.id === UI.screenID)
|
||||
if (screen) {
|
||||
document.getElementById('noVNC_identify_monitor').innerHTML = screen.num
|
||||
|
@ -274,7 +355,6 @@ const UI = {
|
|||
if (UI.rfb) {
|
||||
UI.rfb.disconnect();
|
||||
if (UI.supportsBroadcastChannel) {
|
||||
console.log('remove event listeners')
|
||||
UI.controlChannel.removeEventListener('message', UI.handleControlMessage);
|
||||
UI.rfb.removeEventListener("connect", UI.connectFinished);
|
||||
}
|
||||
|
@ -329,5 +409,9 @@ const UI = {
|
|||
}
|
||||
|
||||
UI.prime();
|
||||
const initialAutoPlacementValue = window.localStorage.getItem('autoPlacement')
|
||||
|
||||
if ('getScreenDetails' in window && initialAutoPlacementValue === null) {
|
||||
UI.connect();
|
||||
}
|
||||
export default UI;
|
||||
|
|
29
core/rfb.js
29
core/rfb.js
|
@ -743,16 +743,16 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
attachSecondaryDisplay() {
|
||||
attachSecondaryDisplay(details) {
|
||||
this._updateConnectionState('connecting');
|
||||
const screen = this._registerSecondaryDisplay();
|
||||
const screen = this._registerSecondaryDisplay(false, details);
|
||||
this._updateConnectionState('connected');
|
||||
return screen
|
||||
}
|
||||
|
||||
reattachSecondaryDisplay(screen) {
|
||||
reattachSecondaryDisplay(screen, details) {
|
||||
this._updateConnectionState('connecting');
|
||||
this._registerSecondaryDisplay(screen);
|
||||
this._registerSecondaryDisplay(screen, details);
|
||||
this._updateConnectionState('connected');
|
||||
return screen
|
||||
}
|
||||
|
@ -1543,7 +1543,15 @@ export default class RFB extends EventTargetMixin {
|
|||
size.serverWidth + 'x' + size.serverHeight);
|
||||
} else if (this._display.screenIndex > 0) {
|
||||
//re-register the secondary display with new resolution
|
||||
this._registerSecondaryDisplay();
|
||||
let details = null
|
||||
const initialAutoPlacementValue = window.localStorage.getItem('autoPlacement')
|
||||
if (initialAutoPlacementValue === null) {
|
||||
details = {
|
||||
left: window.screenLeft,
|
||||
top: window.screenTop
|
||||
}
|
||||
}
|
||||
this._registerSecondaryDisplay(false, details);
|
||||
}
|
||||
|
||||
if (this._display.screens.length > 1) {
|
||||
|
@ -1729,12 +1737,16 @@ export default class RFB extends EventTargetMixin {
|
|||
let coords;
|
||||
switch (event.data.eventType) {
|
||||
case 'register':
|
||||
const details = {
|
||||
...event.data.details,
|
||||
screenID: event.data.screenID
|
||||
}
|
||||
this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth);
|
||||
size = this._screenSize();
|
||||
RFB.messages.setDesktopSize(this._sock, size, this._screenFlags);
|
||||
this._sendEncodings();
|
||||
this._updateContinuousUpdates();
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", { detail: details }));
|
||||
Log.Info(`Secondary monitor (${event.data.screenID}) has been registered.`);
|
||||
break;
|
||||
case 'reattach':
|
||||
|
@ -1843,7 +1855,7 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
}
|
||||
|
||||
_registerSecondaryDisplay(currentScreen = false) {
|
||||
_registerSecondaryDisplay(currentScreen = false, details = null) {
|
||||
if (!this._isPrimaryDisplay) {
|
||||
//let screen = this._screenSize().screens[0];
|
||||
//
|
||||
|
@ -1864,7 +1876,8 @@ export default class RFB extends EventTargetMixin {
|
|||
pixelRatio: screen.pixelRatio,
|
||||
containerWidth: screen.containerWidth,
|
||||
containerHeight: screen.containerHeight,
|
||||
channel: null
|
||||
channel: null,
|
||||
details
|
||||
}
|
||||
this._controlChannel.postMessage(message);
|
||||
|
||||
|
|
80
screen.html
80
screen.html
|
@ -44,6 +44,7 @@
|
|||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="app/styles/base.css">
|
||||
<link rel="stylesheet" href="app/styles/screen.css">
|
||||
|
||||
<script src="app/error-handler.js"></script>
|
||||
|
||||
|
@ -58,7 +59,7 @@
|
|||
|
||||
<script type="module" crossorigin="use-credentials" src="app/ui_screen.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body id="screen">
|
||||
<div id="noVNC_fallback_error" class="noVNC_center">
|
||||
<div>
|
||||
<div id="noVNC_close_error" onclick="document.getElementById('noVNC_fallback_error').remove()"></div>
|
||||
|
@ -107,4 +108,81 @@
|
|||
autocomplete="off" spellcheck="false" tabindex="-1"></textarea>
|
||||
</div>
|
||||
<div id="noVNC_identify_monitor">0</div>
|
||||
|
||||
<div id="mySidenav" class="loadin">
|
||||
<div id="menuTab" class="tab touch-none" style="top: 49px;">
|
||||
<div class="flex flex-col tabinner rounded-r-lg">
|
||||
<div id="dragHandler" class="flex justify-center cursor-move items-center bg-black/5 rounded-tr-lg py-1">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="grip-dots"
|
||||
class="svg-inline--fa fa-grip-dots " role="img" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512">
|
||||
<path fill="currentColor"
|
||||
d="M384 192a16 16 0 1 0 0-32 16 16 0 1 0 0 32zm0-64a48 48 0 1 1 0 96 48 48 0 1 1 0-96zM224 192a16 16 0 1 0 0-32 16 16 0 1 0 0 32zm0-64a48 48 0 1 1 0 96 48 48 0 1 1 0-96zM48 176a16 16 0 1 0 32 0 16 16 0 1 0 -32 0zm64 0a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zM384 352a16 16 0 1 0 0-32 16 16 0 1 0 0 32zm0-64a48 48 0 1 1 0 96 48 48 0 1 1 0-96zM208 336a16 16 0 1 0 32 0 16 16 0 1 0 -32 0zm64 0a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zM64 352a16 16 0 1 0 0-32 16 16 0 1 0 0 32zm0-64a48 48 0 1 1 0 96 48 48 0 1 1 0-96z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="toggleMenu" class="cursor-pointer relative rounded-br-lg innertab">
|
||||
<div id="toggleMenuIcon" class="flex absolute">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="chevron-right"
|
||||
class="svg-inline--fa fa-chevron-right " role="img" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 320 512">
|
||||
<path fill="currentColor"
|
||||
d="M299.3 244.7c6.2 6.2 6.2 16.4 0 22.6l-192 192c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6L265.4 256 84.7 75.3c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l192 192z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidenavnew">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-xl font-bold">Control Panel</span>
|
||||
<button id="closeMenu" class="bg-transparent">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="xmark"
|
||||
class="svg-inline--fa fa-xmark text-xl" role="img" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512">
|
||||
<path fill="currentColor"
|
||||
d="M324.5 411.1c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6L214.6 256 347.1 123.5c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L192 233.4 59.5 100.9c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6L169.4 256 36.9 388.5c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L192 278.6 324.5 411.1z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-icons-list">
|
||||
<div class="grid grid-cols-4 py-5">
|
||||
<button id="fullscreenTrigger" class="sidebar-icons !bg-transparent flex flex-col">
|
||||
<div id="fullscreenBg" class="sidebar-icon-container text-white bg-gray-500"><svg aria-hidden="true" focusable="false"
|
||||
data-prefix="fal" data-icon="expand" class="svg-inline--fa fa-expand fa-lg " role="img"
|
||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path fill="currentColor"
|
||||
d="M144 32c8.8 0 16 7.2 16 16s-7.2 16-16 16H32V176c0 8.8-7.2 16-16 16s-16-7.2-16-16V48c0-8.8 7.2-16 16-16H144zM0 336c0-8.8 7.2-16 16-16s16 7.2 16 16V448H144c8.8 0 16 7.2 16 16s-7.2 16-16 16H16c-8.8 0-16-7.2-16-16V336zM432 32c8.8 0 16 7.2 16 16V176c0 8.8-7.2 16-16 16s-16-7.2-16-16V64H304c-8.8 0-16-7.2-16-16s7.2-16 16-16H432zM416 336c0-8.8 7.2-16 16-16s16 7.2 16 16V464c0 8.8-7.2 16-16 16H304c-8.8 0-16-7.2-16-16s7.2-16 16-16H416V336z">
|
||||
</path>
|
||||
</svg></div>
|
||||
<div class="flex flex-col">
|
||||
<div class="font-bold text-xs">Fullscreen</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function fullscreenchanged(event) {
|
||||
if (document.fullscreenElement) {
|
||||
if ('keyboard' in navigator) {
|
||||
navigator.keyboard.lock(["Escape"]);
|
||||
}
|
||||
document.getElementById('fullscreenBg').classList.remove('bg-gray-500')
|
||||
document.getElementById('fullscreenBg').classList.add('bg-emerald-500')
|
||||
} else {
|
||||
if ('keyboard' in navigator) {
|
||||
navigator.keyboard.unlock(["Escape"]);
|
||||
}
|
||||
document.getElementById('fullscreenBg').classList.remove('bg-emerald-500')
|
||||
document.getElementById('fullscreenBg').classList.add('bg-gray-500')
|
||||
}
|
||||
}
|
||||
document.addEventListener("fullscreenchange", fullscreenchanged);
|
||||
|
||||
</script>
|
||||
</body>
|
6
vnc.html
6
vnc.html
|
@ -553,6 +553,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"><path fill="currentColor" d="M240 64c0-8.8-7.2-16-16-16s-16 7.2-16 16V240H32c-8.8 0-16 7.2-16 16s7.2 16 16 16H208V448c0 8.8 7.2 16 16 16s16-7.2 16-16V272H416c8.8 0 16-7.2 16-16s-7.2-16-16-16H240V64z"/></svg>
|
||||
Add Monitor
|
||||
</button>
|
||||
|
||||
<label id="noVNC_auto_placement_option" class="button">
|
||||
<input style="margin: 0 10px 0 3px;" id="noVNC_auto_placement" type="checkbox" />
|
||||
Auto placement
|
||||
</label>
|
||||
|
||||
<label id="noVNC_setting_enable_hidpi_option" class="button">
|
||||
<input style="margin: 0 10px 0 3px;" id="noVNC_setting_enable_hidpi" type="checkbox" />
|
||||
Native Resolution
|
||||
|
|
Loading…
Reference in New Issue