mirror of
https://github.com/kbumsik/VirtScreen.git
synced 2025-03-09 15:40:18 +00:00
Optimization: Use loader to reduce memory on idle
This commit is contained in:
parent
863cb99124
commit
fe9b3fb13e
3 changed files with 621 additions and 641 deletions
683
main.qml
683
main.qml
|
@ -1,70 +1,24 @@
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.3
|
|
||||||
import QtQuick.Controls.Material 2.3
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
|
|
||||||
import Qt.labs.platform 1.0 as Labs
|
import Qt.labs.platform 1.0
|
||||||
|
|
||||||
import VirtScreen.DisplayProperty 1.0
|
import VirtScreen.DisplayProperty 1.0
|
||||||
import VirtScreen.Backend 1.0
|
import VirtScreen.Backend 1.0
|
||||||
|
|
||||||
|
Loader {
|
||||||
ApplicationWindow {
|
id: mainLoader
|
||||||
id: window
|
active: false
|
||||||
visible: false
|
source: "mainWindow.qml"
|
||||||
flags: Qt.FramelessWindowHint
|
property alias window: mainLoader.item
|
||||||
title: "Basic layouts"
|
|
||||||
|
|
||||||
Material.theme: Material.Light
|
|
||||||
Material.primary: Material.Teal
|
|
||||||
Material.accent: Material.Teal
|
|
||||||
// Material.background: Material.Grey
|
|
||||||
|
|
||||||
width: 380
|
|
||||||
height: 525
|
|
||||||
property int margin: 8
|
|
||||||
property int popupWidth: width - 26
|
|
||||||
|
|
||||||
// hide screen when loosing focus
|
|
||||||
property bool autoClose: true
|
|
||||||
property bool ignoreCloseOnce: false
|
|
||||||
onAutoCloseChanged: {
|
|
||||||
// When setting auto close disabled and then enabled again, we need to
|
|
||||||
// ignore focus change once. Otherwise the window always is closed one time
|
|
||||||
// even when the mouse is clicked in the window.
|
|
||||||
if (!autoClose) {
|
|
||||||
ignoreCloseOnce = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onActiveFocusItemChanged: {
|
|
||||||
if (autoClose && !ignoreCloseOnce && !activeFocusItem && !sysTrayIcon.clicked) {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
if (ignoreCloseOnce && autoClose) {
|
|
||||||
ignoreCloseOnce = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// One-shot signal connect
|
|
||||||
function connectOnce (signal, slot) {
|
|
||||||
var f = function() {
|
|
||||||
slot.apply(this, arguments);
|
|
||||||
signal.disconnect(f);
|
|
||||||
}
|
|
||||||
signal.connect(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtscreen.py backend.
|
// virtscreen.py backend.
|
||||||
Backend {
|
Backend {
|
||||||
id: backend
|
id: backend
|
||||||
|
|
||||||
function switchVNC () {
|
function switchVNC () {
|
||||||
if ((backend.vncState == Backend.OFF) && backend.virtScreenCreated) {
|
if ((backend.vncState == Backend.OFF) && backend.virtScreenCreated) {
|
||||||
backend.startVNC();
|
backend.startVNC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onVncAutoStartChanged: {
|
onVncAutoStartChanged: {
|
||||||
if (vncAutoStart) {
|
if (vncAutoStart) {
|
||||||
onVirtScreenCreatedChanged.connect(switchVNC);
|
onVirtScreenCreatedChanged.connect(switchVNC);
|
||||||
|
@ -74,14 +28,12 @@ ApplicationWindow {
|
||||||
onVncStateChanged.disconnect(switchVNC);
|
onVncStateChanged.disconnect(switchVNC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// force emit signal on load
|
// force emit signal on load
|
||||||
vncAutoStart = vncAutoStart;
|
vncAutoStart = vncAutoStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Timer object and function
|
// Timer object and function
|
||||||
Timer {
|
Timer {
|
||||||
id: timer
|
id: timer
|
||||||
|
@ -96,304 +48,62 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// menuBar: MenuBar {
|
// One-shot signal connect
|
||||||
// }
|
function connectOnce (signal, slot) {
|
||||||
|
var f = function() {
|
||||||
|
slot.apply(this, arguments);
|
||||||
|
signal.disconnect(f);
|
||||||
|
}
|
||||||
|
signal.connect(f);
|
||||||
|
}
|
||||||
|
|
||||||
menuBar: ToolBar {
|
// Sytray Icon
|
||||||
id: toolbar
|
SystemTrayIcon {
|
||||||
font.weight: Font.Medium
|
id: sysTrayIcon
|
||||||
font.pointSize: 11 //parent.font.pointSize + 1
|
iconSource: backend.vncState == Backend.CONNECTED ? "icon/icon_tablet_on.png" :
|
||||||
|
backend.virtScreenCreated ? "icon/icon_tablet_off.png" :
|
||||||
|
"icon/icon.png"
|
||||||
|
visible: true
|
||||||
|
property bool clicked: false
|
||||||
|
onMessageClicked: console.log("Message clicked")
|
||||||
|
Component.onCompleted: {
|
||||||
|
// without delay, the message appears in a wierd place
|
||||||
|
timer.setTimeout (function() {
|
||||||
|
showMessage("VirtScreen is running",
|
||||||
|
"The program will keep running in the system tray.\n" +
|
||||||
|
"To terminate the program, choose \"Quit\" in the \n" +
|
||||||
|
"context menu of the system tray entry.");
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
onActivated: function(reason) {
|
||||||
anchors.fill: parent
|
console.log(reason);
|
||||||
anchors.leftMargin: margin + 10
|
if (reason == SystemTrayIcon.Context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysTrayIcon.clicked = true;
|
||||||
|
mainLoader.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
menu: Menu {
|
||||||
id: vncStateLabel
|
MenuItem {
|
||||||
|
id: vncStateText
|
||||||
|
enabled: false
|
||||||
text: !backend.virtScreenCreated ? "Enable Virtual Screen first." :
|
text: !backend.virtScreenCreated ? "Enable Virtual Screen first." :
|
||||||
backend.vncState == Backend.OFF ? "Turn on VNC Server in the VNC tab." :
|
backend.vncState == Backend.OFF ? "Turn on VNC Server in the VNC tab." :
|
||||||
backend.vncState == Backend.WAITING ? "VNC Server is waiting for a client..." :
|
backend.vncState == Backend.WAITING ? "VNC Server is waiting for a client..." :
|
||||||
backend.vncState == Backend.CONNECTED ? "Connected." :
|
backend.vncState == Backend.CONNECTED ? "Connected." :
|
||||||
"Server state error!"
|
"Server state error!"
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
ToolButton {
|
separator: true
|
||||||
id: menuButton
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: qsTr("⋮")
|
|
||||||
onClicked: menu.open()
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: menu
|
|
||||||
y: toolbar.height
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
text: qsTr("&About")
|
|
||||||
onTriggered: {
|
|
||||||
aboutDialog.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
text: qsTr("&Quit")
|
|
||||||
onTriggered: {
|
|
||||||
backend.quitProgram();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
MenuItem {
|
||||||
}
|
id: virtScreenAction
|
||||||
|
|
||||||
header: TabBar {
|
|
||||||
id: tabBar
|
|
||||||
position: TabBar.Footer
|
|
||||||
// Material.primary: Material.Teal
|
|
||||||
|
|
||||||
currentIndex: 0
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: qsTr("Display")
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: qsTr("VNC")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// footer: ToolBar {
|
|
||||||
// font.weight: Font.Medium
|
|
||||||
// font.pointSize: 11 //parent.font.pointSize + 1
|
|
||||||
// anchors { horizontalCenter: parent.horizontalCenter }
|
|
||||||
// width: 200
|
|
||||||
// }
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
id: busyDialog
|
|
||||||
modal: true
|
|
||||||
closePolicy: Popup.NoAutoClose
|
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: parent.height / 2 - height
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
anchors.fill: parent
|
|
||||||
Material.accent: Material.Cyan
|
|
||||||
running: true
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 100
|
|
||||||
implicitHeight: 100
|
|
||||||
// border.color: "#444"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: aboutDialog
|
|
||||||
focus: true
|
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: (parent.width - height) / 2 //(window.height) / 2
|
|
||||||
width: popupWidth
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font { weight: Font.Bold; pointSize: 15 }
|
|
||||||
text: "VirtScreen"
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: "Make your iPad/tablet/computer<br/>as a secondary monitor.<br/>"
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
text: "- <a href='https://github.com/kbumsik/VirtScreen'>Project Website</a>"
|
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
text: "- <a href='https://github.com/kbumsik/VirtScreen/issues'>Issues & Bug Report</a>"
|
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
font { pointSize: 10 }
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
lineHeight: 0.7
|
|
||||||
text: "<br/>Copyright © 2018 Bumsik Kim <a href='https://kbumsik.io/'>Homepage</a><br/>"
|
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
font { pointSize: 9 }
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: "This program comes with absolutely no warranty.<br/>" +
|
|
||||||
"See the <a href='https://github.com/kbumsik/VirtScreen/blob/master/LICENSE'>" +
|
|
||||||
"GNU General Public License, version 3</a> for details."
|
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: passwordDialog
|
|
||||||
title: "New password"
|
|
||||||
focus: true
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: (parent.width - height) / 2 //(window.height) / 2
|
|
||||||
width: popupWidth
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: passwordFIeld
|
|
||||||
focus: true
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
placeholderText: "New Password";
|
|
||||||
echoMode: TextInput.Password;
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: {
|
|
||||||
event.accepted = true;
|
|
||||||
if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) {
|
|
||||||
passwordDialog.accept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
backend.createVNCPassword(passwordFIeld.text);
|
|
||||||
passwordFIeld.text = "";
|
|
||||||
}
|
|
||||||
onRejected: passwordFIeld.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
width: parent.width
|
|
||||||
anchors.top: tabBar.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
currentIndex: tabBar.currentIndex
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: margin
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
title: "Virtual Display"
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
enabled: backend.virtScreenCreated ? false : true
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label { text: "Width"; Layout.fillWidth: true }
|
|
||||||
SpinBox {
|
|
||||||
value: backend.virt.width
|
|
||||||
from: 640
|
|
||||||
to: 1920
|
|
||||||
stepSize: 1
|
|
||||||
editable: true
|
|
||||||
onValueModified: {
|
|
||||||
backend.virt.width = value;
|
|
||||||
}
|
|
||||||
textFromValue: function(value, locale) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label { text: "Height"; Layout.fillWidth: true }
|
|
||||||
SpinBox {
|
|
||||||
value: backend.virt.height
|
|
||||||
from: 360
|
|
||||||
to: 1080
|
|
||||||
stepSize : 1
|
|
||||||
editable: true
|
|
||||||
onValueModified: {
|
|
||||||
backend.virt.height = value;
|
|
||||||
}
|
|
||||||
textFromValue: function(value, locale) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label { text: "Portrait Mode"; Layout.fillWidth: true }
|
|
||||||
Switch {
|
|
||||||
checked: backend.portrait
|
|
||||||
onCheckedChanged: {
|
|
||||||
backend.portrait = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label { text: "HiDPI (2x resolution)"; Layout.fillWidth: true }
|
|
||||||
Switch {
|
|
||||||
checked: backend.hidpi
|
|
||||||
onCheckedChanged: {
|
|
||||||
backend.hidpi = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
Label { id: deviceLabel; text: "Device"; }
|
|
||||||
ComboBox {
|
|
||||||
id: deviceComboBox
|
|
||||||
anchors.left: deviceLabel.right
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 100
|
|
||||||
|
|
||||||
textRole: "name"
|
|
||||||
model: backend.screens
|
|
||||||
currentIndex: backend.virtScreenIndex
|
|
||||||
|
|
||||||
onActivated: function(index) {
|
|
||||||
backend.virtScreenIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
|
||||||
width: deviceComboBox.width
|
|
||||||
text: modelData.name
|
|
||||||
font.weight: deviceComboBox.currentIndex === index ? Font.DemiBold : Font.Normal
|
|
||||||
highlighted: ListView.isCurrentItem
|
|
||||||
enabled: modelData.connected ? false : true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: virtScreenButton
|
|
||||||
text: backend.virtScreenCreated ? "Disable Virtual Screen" : "Enable Virtual Screen"
|
text: backend.virtScreenCreated ? "Disable Virtual Screen" : "Enable Virtual Screen"
|
||||||
highlighted: true
|
enabled:backend.vncAutoStart ? true :
|
||||||
|
backend.vncState == Backend.OFF ? true : false
|
||||||
anchors.left: parent.left
|
onTriggered: {
|
||||||
anchors.right: parent.right
|
|
||||||
// Material.accent: Material.Teal
|
|
||||||
// Material.theme: Material.Dark
|
|
||||||
|
|
||||||
enabled: backend.vncAutoStart ? true :
|
|
||||||
backend.vncState == Backend.OFF ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
busyDialog.open();
|
|
||||||
// Give a very short delay to show busyDialog.
|
// Give a very short delay to show busyDialog.
|
||||||
timer.setTimeout (function() {
|
timer.setTimeout (function() {
|
||||||
if (!backend.virtScreenCreated) {
|
if (!backend.virtScreenCreated) {
|
||||||
|
@ -417,263 +127,19 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
backend.onVirtScreenCreatedChanged.connect(function(created) {
|
|
||||||
busyDialog.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
Button {
|
id: vncAction
|
||||||
id: displaySettingButton
|
|
||||||
text: "Open Display Setting"
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
// Material.accent: Material.Teal
|
|
||||||
// Material.theme: Material.Dark
|
|
||||||
|
|
||||||
enabled: backend.virtScreenCreated ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
busyDialog.open();
|
|
||||||
window.autoClose = false;
|
|
||||||
if (backend.vncState != Backend.OFF) {
|
|
||||||
console.log("vnc is running");
|
|
||||||
var restoreVNC = true;
|
|
||||||
if (backend.vncAutoStart) {
|
|
||||||
backend.vncAutoStart = false;
|
|
||||||
var restoreAutoStart = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connectOnce(backend.onDisplaySettingClosed, function() {
|
|
||||||
window.autoClose = true;
|
|
||||||
busyDialog.close();
|
|
||||||
if (restoreAutoStart) {
|
|
||||||
backend.vncAutoStart = true;
|
|
||||||
}
|
|
||||||
if (restoreVNC) {
|
|
||||||
backend.startVNC();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
backend.stopVNC();
|
|
||||||
backend.openDisplaySetting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: margin
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
title: "VNC Server"
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
enabled: backend.vncState == Backend.OFF ? true : false
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label { text: "Port"; Layout.fillWidth: true }
|
|
||||||
SpinBox {
|
|
||||||
value: backend.vncPort
|
|
||||||
from: 1
|
|
||||||
to: 65535
|
|
||||||
stepSize: 1
|
|
||||||
editable: true
|
|
||||||
onValueModified: {
|
|
||||||
backend.vncPort = value;
|
|
||||||
}
|
|
||||||
textFromValue: function(value, locale) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
Label { text: "Password"; Layout.fillWidth: true }
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Delete"
|
|
||||||
font.capitalization: Font.MixedCase
|
|
||||||
highlighted: false
|
|
||||||
enabled: backend.vncUsePassword
|
|
||||||
onClicked: backend.deleteVNCPassword()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "New"
|
|
||||||
font.capitalization: Font.MixedCase
|
|
||||||
highlighted: true
|
|
||||||
enabled: !backend.vncUsePassword
|
|
||||||
onClicked: passwordDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: vncButton
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottomMargin: 0
|
|
||||||
highlighted: true
|
|
||||||
|
|
||||||
text: backend.vncAutoStart ? "Auto start enabled" :
|
text: backend.vncAutoStart ? "Auto start enabled" :
|
||||||
backend.vncState == Backend.OFF ? "Start VNC Server" : "Stop VNC Server"
|
backend.vncState == Backend.OFF ? "Start VNC Server" : "Stop VNC Server"
|
||||||
enabled: backend.vncAutoStart ? false :
|
enabled: backend.vncAutoStart ? false :
|
||||||
backend.virtScreenCreated ? true : false
|
backend.virtScreenCreated ? true : false
|
||||||
// Material.background: Material.Teal
|
onTriggered: backend.vncState == Backend.OFF ? backend.startVNC() : backend.stopVNC()
|
||||||
// Material.foreground: Material.Grey
|
|
||||||
onClicked: backend.vncState == Backend.OFF ? backend.startVNC() : backend.stopVNC()
|
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
RowLayout {
|
|
||||||
id: autoSwitchLayout
|
|
||||||
anchors.top: vncButton.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: vncButton.height - 10
|
|
||||||
|
|
||||||
Label { text: "Auto start"; }
|
|
||||||
Switch {
|
|
||||||
checked: backend.vncAutoStart
|
|
||||||
onToggled: {
|
|
||||||
if ((checked == true) && (backend.vncState == Backend.OFF) &&
|
|
||||||
backend.virtScreenCreated) {
|
|
||||||
backend.startVNC();
|
|
||||||
}
|
|
||||||
backend.vncAutoStart = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
title: "Available IP addresses"
|
|
||||||
anchors.top: autoSwitchLayout.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: ipListView
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
// anchors.top: parent.top
|
|
||||||
// anchors.left: parent.left
|
|
||||||
// anchors.right: parent.right
|
|
||||||
// height: 100
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
parent: ipListView.parent
|
|
||||||
anchors.top: ipListView.top
|
|
||||||
anchors.right: ipListView.right
|
|
||||||
anchors.bottom: ipListView.bottom
|
|
||||||
policy: ScrollBar.AlwaysOn
|
|
||||||
}
|
|
||||||
|
|
||||||
model: backend.ipAddresses
|
|
||||||
delegate: TextEdit {
|
|
||||||
text: modelData
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
font.pointSize: 12
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sytray Icon
|
|
||||||
Labs.SystemTrayIcon {
|
|
||||||
id: sysTrayIcon
|
|
||||||
iconSource: backend.vncState == Backend.CONNECTED ? "icon/icon_tablet_on.png" :
|
|
||||||
backend.virtScreenCreated ? "icon/icon_tablet_off.png" :
|
|
||||||
"icon/icon.png"
|
|
||||||
visible: true
|
|
||||||
property bool clicked: false
|
|
||||||
|
|
||||||
onMessageClicked: console.log("Message clicked")
|
|
||||||
Component.onCompleted: {
|
|
||||||
// without delay, the message appears in a wierd place
|
|
||||||
timer.setTimeout (function() {
|
|
||||||
showMessage("VirtScreen is running",
|
|
||||||
"The program will keep running in the system tray.\n" +
|
|
||||||
"To terminate the program, choose \"Quit\" in the \n" +
|
|
||||||
"context menu of the system tray entry.");
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
onActivated: function(reason) {
|
|
||||||
console.log(reason);
|
|
||||||
if (reason == Labs.SystemTrayIcon.Context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (window.visible) {
|
|
||||||
window.hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sysTrayIcon.clicked = true;
|
|
||||||
// Move window to the corner of the primary display
|
|
||||||
var primary = backend.primary;
|
|
||||||
var width = primary.width;
|
|
||||||
var height = primary.height;
|
|
||||||
var cursor_x = backend.cursor_x - primary.x_offset;
|
|
||||||
var cursor_y = backend.cursor_y - primary.y_offset;
|
|
||||||
var x_mid = width / 2;
|
|
||||||
var y_mid = height / 2;
|
|
||||||
var x = width - window.width; //(cursor_x > x_mid)? width - window.width : 0;
|
|
||||||
var y = (cursor_y > y_mid)? height - window.height : 0;
|
|
||||||
x += primary.x_offset;
|
|
||||||
y += primary.y_offset;
|
|
||||||
window.x = x;
|
|
||||||
window.y = y;
|
|
||||||
window.show();
|
|
||||||
window.raise();
|
|
||||||
window.requestActivate();
|
|
||||||
timer.setTimeout (function() {
|
|
||||||
sysTrayIcon.clicked = false;
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
menu: Labs.Menu {
|
|
||||||
Labs.MenuItem {
|
|
||||||
enabled: false
|
|
||||||
text: vncStateLabel.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Labs.MenuItem {
|
|
||||||
separator: true
|
separator: true
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
Labs.MenuItem {
|
|
||||||
text: virtScreenButton.text
|
|
||||||
enabled: virtScreenButton.enabled
|
|
||||||
onTriggered: virtScreenButton.onClicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
Labs.MenuItem {
|
|
||||||
text: vncButton.text
|
|
||||||
enabled: vncButton.enabled
|
|
||||||
onTriggered: vncButton.onClicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
Labs.MenuItem {
|
|
||||||
separator: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Labs.MenuItem {
|
|
||||||
text: qsTr("&Quit")
|
text: qsTr("&Quit")
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
backend.quitProgram();
|
backend.quitProgram();
|
||||||
|
@ -681,4 +147,41 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
console.log("Status changed", status);
|
||||||
|
if (status == Loader.Null) {
|
||||||
|
gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
window.onVisibleChanged.connect(function(visible) {
|
||||||
|
if (!visible) {
|
||||||
|
console.log('hiding');
|
||||||
|
console.log("unloading...");
|
||||||
|
mainLoader.active = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Move window to the corner of the primary display
|
||||||
|
var primary = backend.primary;
|
||||||
|
var width = primary.width;
|
||||||
|
var height = primary.height;
|
||||||
|
var cursor_x = backend.cursor_x - primary.x_offset;
|
||||||
|
var cursor_y = backend.cursor_y - primary.y_offset;
|
||||||
|
var x_mid = width / 2;
|
||||||
|
var y_mid = height / 2;
|
||||||
|
var x = width - window.width; //(cursor_x > x_mid)? width - window.width : 0;
|
||||||
|
var y = (cursor_y > y_mid)? height - window.height : 0;
|
||||||
|
x += primary.x_offset;
|
||||||
|
y += primary.y_offset;
|
||||||
|
window.x = x;
|
||||||
|
window.y = y;
|
||||||
|
window.show();
|
||||||
|
window.raise();
|
||||||
|
window.requestActivate();
|
||||||
|
timer.setTimeout (function() {
|
||||||
|
sysTrayIcon.clicked = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
}
|
}
|
463
mainWindow.qml
Normal file
463
mainWindow.qml
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
import QtQuick 2.10
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
import QtQuick.Controls.Material 2.3
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import VirtScreen.Backend 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: window
|
||||||
|
visible: false
|
||||||
|
flags: Qt.FramelessWindowHint
|
||||||
|
title: "Basic layouts"
|
||||||
|
|
||||||
|
Material.theme: Material.Light
|
||||||
|
Material.primary: Material.Teal
|
||||||
|
Material.accent: Material.Teal
|
||||||
|
// Material.background: Material.Grey
|
||||||
|
|
||||||
|
width: 380
|
||||||
|
height: 525
|
||||||
|
property int margin: 8
|
||||||
|
property int popupWidth: width - 26
|
||||||
|
|
||||||
|
// hide screen when loosing focus
|
||||||
|
property bool autoClose: true
|
||||||
|
property bool ignoreCloseOnce: false
|
||||||
|
onAutoCloseChanged: {
|
||||||
|
// When setting auto close disabled and then enabled again, we need to
|
||||||
|
// ignore focus change once. Otherwise the window always is closed one time
|
||||||
|
// even when the mouse is clicked in the window.
|
||||||
|
if (!autoClose) {
|
||||||
|
ignoreCloseOnce = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onActiveFocusItemChanged: {
|
||||||
|
if (autoClose && !ignoreCloseOnce && !activeFocusItem && !sysTrayIcon.clicked) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
if (ignoreCloseOnce && autoClose) {
|
||||||
|
ignoreCloseOnce = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar: ToolBar {
|
||||||
|
id: toolbar
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pointSize: 11 //parent.font.pointSize + 1
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: margin + 10
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: vncStateLabel
|
||||||
|
text: vncStateText.text
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: menuButton
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: qsTr("⋮")
|
||||||
|
onClicked: menu.open()
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: menu
|
||||||
|
y: toolbar.height
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("&About")
|
||||||
|
onTriggered: {
|
||||||
|
aboutDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("&Quit")
|
||||||
|
onTriggered: {
|
||||||
|
backend.quitProgram();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header: TabBar {
|
||||||
|
id: tabBar
|
||||||
|
position: TabBar.Footer
|
||||||
|
// Material.primary: Material.Teal
|
||||||
|
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("Display")
|
||||||
|
}
|
||||||
|
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("VNC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// footer: ToolBar {
|
||||||
|
// font.weight: Font.Medium
|
||||||
|
// font.pointSize: 11 //parent.font.pointSize + 1
|
||||||
|
// anchors { horizontalCenter: parent.horizontalCenter }
|
||||||
|
// width: 200
|
||||||
|
// }
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: busyDialog
|
||||||
|
modal: true
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: parent.height / 2 - height
|
||||||
|
BusyIndicator {
|
||||||
|
anchors.fill: parent
|
||||||
|
Material.accent: Material.Cyan
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: 100
|
||||||
|
implicitHeight: 100
|
||||||
|
// border.color: "#444"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: aboutDialog
|
||||||
|
focus: true
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.width - height) / 2 //(window.height) / 2
|
||||||
|
width: popupWidth
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
Text {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font { weight: Font.Bold; pointSize: 15 }
|
||||||
|
text: "VirtScreen"
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: "Make your iPad/tablet/computer<br/>as a secondary monitor.<br/>"
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: "- <a href='https://github.com/kbumsik/VirtScreen'>Project Website</a>"
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: "- <a href='https://github.com/kbumsik/VirtScreen/issues'>Issues & Bug Report</a>"
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
font { pointSize: 10 }
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
lineHeight: 0.7
|
||||||
|
text: "<br/>Copyright © 2018 Bumsik Kim <a href='https://kbumsik.io/'>Homepage</a><br/>"
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
font { pointSize: 9 }
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: "This program comes with absolutely no warranty.<br/>" +
|
||||||
|
"See the <a href='https://github.com/kbumsik/VirtScreen/blob/master/LICENSE'>" +
|
||||||
|
"GNU General Public License, version 3</a> for details."
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: passwordDialog
|
||||||
|
title: "New password"
|
||||||
|
focus: true
|
||||||
|
modal: true
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.width - height) / 2 //(window.height) / 2
|
||||||
|
width: popupWidth
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
TextField {
|
||||||
|
id: passwordFIeld
|
||||||
|
focus: true
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
placeholderText: "New Password";
|
||||||
|
echoMode: TextInput.Password;
|
||||||
|
}
|
||||||
|
Keys.onPressed: {
|
||||||
|
event.accepted = true;
|
||||||
|
if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) {
|
||||||
|
passwordDialog.accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
backend.createVNCPassword(passwordFIeld.text);
|
||||||
|
passwordFIeld.text = "";
|
||||||
|
}
|
||||||
|
onRejected: passwordFIeld.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: tabBar.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
currentIndex: tabBar.currentIndex
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: margin
|
||||||
|
GroupBox {
|
||||||
|
title: "Virtual Display"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
enabled: backend.virtScreenCreated ? false : true
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "Width"; Layout.fillWidth: true }
|
||||||
|
SpinBox {
|
||||||
|
value: backend.virt.width
|
||||||
|
from: 640
|
||||||
|
to: 1920
|
||||||
|
stepSize: 1
|
||||||
|
editable: true
|
||||||
|
onValueModified: {
|
||||||
|
backend.virt.width = value;
|
||||||
|
}
|
||||||
|
textFromValue: function(value, locale) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "Height"; Layout.fillWidth: true }
|
||||||
|
SpinBox {
|
||||||
|
value: backend.virt.height
|
||||||
|
from: 360
|
||||||
|
to: 1080
|
||||||
|
stepSize : 1
|
||||||
|
editable: true
|
||||||
|
onValueModified: {
|
||||||
|
backend.virt.height = value;
|
||||||
|
}
|
||||||
|
textFromValue: function(value, locale) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "Portrait Mode"; Layout.fillWidth: true }
|
||||||
|
Switch {
|
||||||
|
checked: backend.portrait
|
||||||
|
onCheckedChanged: {
|
||||||
|
backend.portrait = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "HiDPI (2x resolution)"; Layout.fillWidth: true }
|
||||||
|
Switch {
|
||||||
|
checked: backend.hidpi
|
||||||
|
onCheckedChanged: {
|
||||||
|
backend.hidpi = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Label { id: deviceLabel; text: "Device"; }
|
||||||
|
ComboBox {
|
||||||
|
id: deviceComboBox
|
||||||
|
anchors.left: deviceLabel.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 100
|
||||||
|
textRole: "name"
|
||||||
|
model: backend.screens
|
||||||
|
currentIndex: backend.virtScreenIndex
|
||||||
|
onActivated: function(index) {
|
||||||
|
backend.virtScreenIndex = index
|
||||||
|
}
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
width: deviceComboBox.width
|
||||||
|
text: modelData.name
|
||||||
|
font.weight: deviceComboBox.currentIndex === index ? Font.DemiBold : Font.Normal
|
||||||
|
highlighted: ListView.isCurrentItem
|
||||||
|
enabled: modelData.connected ? false : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: virtScreenButton
|
||||||
|
text: virtScreenAction.text
|
||||||
|
highlighted: true
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
// Material.accent: Material.Teal
|
||||||
|
// Material.theme: Material.Dark
|
||||||
|
enabled: virtScreenAction.enabled
|
||||||
|
onClicked: {
|
||||||
|
busyDialog.open();
|
||||||
|
virtScreenAction.onTriggered();
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
backend.onVirtScreenCreatedChanged.connect(function(created) {
|
||||||
|
busyDialog.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: displaySettingButton
|
||||||
|
text: "Open Display Setting"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
// Material.accent: Material.Teal
|
||||||
|
// Material.theme: Material.Dark
|
||||||
|
enabled: backend.virtScreenCreated ? true : false
|
||||||
|
onClicked: {
|
||||||
|
busyDialog.open();
|
||||||
|
window.autoClose = false;
|
||||||
|
if (backend.vncState != Backend.OFF) {
|
||||||
|
console.log("vnc is running");
|
||||||
|
var restoreVNC = true;
|
||||||
|
if (backend.vncAutoStart) {
|
||||||
|
backend.vncAutoStart = false;
|
||||||
|
var restoreAutoStart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectOnce(backend.onDisplaySettingClosed, function() {
|
||||||
|
window.autoClose = true;
|
||||||
|
busyDialog.close();
|
||||||
|
if (restoreAutoStart) {
|
||||||
|
backend.vncAutoStart = true;
|
||||||
|
}
|
||||||
|
if (restoreVNC) {
|
||||||
|
backend.startVNC();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
backend.stopVNC();
|
||||||
|
backend.openDisplaySetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: margin
|
||||||
|
GroupBox {
|
||||||
|
title: "VNC Server"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
enabled: backend.vncState == Backend.OFF ? true : false
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
RowLayout {
|
||||||
|
Label { text: "Port"; Layout.fillWidth: true }
|
||||||
|
SpinBox {
|
||||||
|
value: backend.vncPort
|
||||||
|
from: 1
|
||||||
|
to: 65535
|
||||||
|
stepSize: 1
|
||||||
|
editable: true
|
||||||
|
onValueModified: {
|
||||||
|
backend.vncPort = value;
|
||||||
|
}
|
||||||
|
textFromValue: function(value, locale) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Label { text: "Password"; Layout.fillWidth: true }
|
||||||
|
Button {
|
||||||
|
text: "Delete"
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
highlighted: false
|
||||||
|
enabled: backend.vncUsePassword
|
||||||
|
onClicked: backend.deleteVNCPassword()
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "New"
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
highlighted: true
|
||||||
|
enabled: !backend.vncUsePassword
|
||||||
|
onClicked: passwordDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: vncButton
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
highlighted: true
|
||||||
|
text: vncAction.text
|
||||||
|
enabled: vncAction.enabled
|
||||||
|
// Material.background: Material.Teal
|
||||||
|
// Material.foreground: Material.Grey
|
||||||
|
onClicked: vncAction.onTriggered()
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: autoSwitchLayout
|
||||||
|
anchors.top: vncButton.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: vncButton.height - 10
|
||||||
|
Label { text: "Auto start"; }
|
||||||
|
Switch {
|
||||||
|
checked: backend.vncAutoStart
|
||||||
|
onToggled: {
|
||||||
|
if ((checked == true) && (backend.vncState == Backend.OFF) &&
|
||||||
|
backend.virtScreenCreated) {
|
||||||
|
backend.startVNC();
|
||||||
|
}
|
||||||
|
backend.vncAutoStart = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupBox {
|
||||||
|
title: "Available IP addresses"
|
||||||
|
anchors.top: autoSwitchLayout.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
ListView {
|
||||||
|
id: ipListView
|
||||||
|
anchors.fill: parent
|
||||||
|
// anchors.top: parent.top
|
||||||
|
// anchors.left: parent.left
|
||||||
|
// anchors.right: parent.right
|
||||||
|
// height: 100
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
parent: ipListView.parent
|
||||||
|
anchors.top: ipListView.top
|
||||||
|
anchors.right: ipListView.right
|
||||||
|
anchors.bottom: ipListView.bottom
|
||||||
|
policy: ScrollBar.AlwaysOn
|
||||||
|
}
|
||||||
|
model: backend.ipAddresses
|
||||||
|
delegate: TextEdit {
|
||||||
|
text: modelData
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
virtscreen.py
108
virtscreen.py
|
@ -131,18 +131,17 @@ class ProcessProtocol(protocol.ProcessProtocol):
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Display properties
|
# Display properties
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
class DisplayProperty(QObject):
|
class Display(object):
|
||||||
_name: str
|
__slots__ = ['name', 'primary', 'connected', 'active', 'width', 'height', 'x_offset', 'y_offset']
|
||||||
_primary: bool
|
|
||||||
_connected: bool
|
|
||||||
_active: bool
|
|
||||||
_width: int
|
|
||||||
_height: int
|
|
||||||
_x_offset: int
|
|
||||||
_y_offset: int
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(DisplayProperty, self).__init__(parent)
|
self.name: str = None
|
||||||
|
self.primary: bool = False
|
||||||
|
self.connected: bool = False
|
||||||
|
self.active: bool = False
|
||||||
|
self.width: int = 0
|
||||||
|
self.height: int = 0
|
||||||
|
self.x_offset: int = 0
|
||||||
|
self.y_offset: int = 0
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
ret = f"{self.name}"
|
ret = f"{self.name}"
|
||||||
|
@ -155,64 +154,76 @@ class DisplayProperty(QObject):
|
||||||
if self.active:
|
if self.active:
|
||||||
ret += f" {self.width}x{self.height}+{self.x_offset}+{self.y_offset}"
|
ret += f" {self.width}x{self.height}+{self.x_offset}+{self.y_offset}"
|
||||||
else:
|
else:
|
||||||
ret += " not active"
|
ret += f" not active {self.width}x{self.height}"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayProperty(QObject):
|
||||||
|
_display: Display
|
||||||
|
|
||||||
|
def __init__(self, display: Display, parent=None):
|
||||||
|
super(DisplayProperty, self).__init__(parent)
|
||||||
|
self._display = display
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display(self):
|
||||||
|
return self._display
|
||||||
|
|
||||||
@pyqtProperty(str, constant=True)
|
@pyqtProperty(str, constant=True)
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._display.name
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._display.name = name
|
||||||
|
|
||||||
@pyqtProperty(bool, constant=True)
|
@pyqtProperty(bool, constant=True)
|
||||||
def primary(self):
|
def primary(self):
|
||||||
return self._primary
|
return self._display.primary
|
||||||
@primary.setter
|
@primary.setter
|
||||||
def primary(self, primary):
|
def primary(self, primary):
|
||||||
self._primary = primary
|
self._display.primary = primary
|
||||||
|
|
||||||
@pyqtProperty(bool, constant=True)
|
@pyqtProperty(bool, constant=True)
|
||||||
def connected(self):
|
def connected(self):
|
||||||
return self._connected
|
return self._display.connected
|
||||||
@connected.setter
|
@connected.setter
|
||||||
def connected(self, connected):
|
def connected(self, connected):
|
||||||
self._connected = connected
|
self._display.connected = connected
|
||||||
|
|
||||||
@pyqtProperty(bool, constant=True)
|
@pyqtProperty(bool, constant=True)
|
||||||
def active(self):
|
def active(self):
|
||||||
return self._active
|
return self._display.active
|
||||||
@active.setter
|
@active.setter
|
||||||
def active(self, active):
|
def active(self, active):
|
||||||
self._active = active
|
self._display.active = active
|
||||||
|
|
||||||
@pyqtProperty(int, constant=True)
|
@pyqtProperty(int, constant=True)
|
||||||
def width(self):
|
def width(self):
|
||||||
return self._width
|
return self._display.width
|
||||||
@width.setter
|
@width.setter
|
||||||
def width(self, width):
|
def width(self, width):
|
||||||
self._width = width
|
self._display.width = width
|
||||||
|
|
||||||
@pyqtProperty(int, constant=True)
|
@pyqtProperty(int, constant=True)
|
||||||
def height(self):
|
def height(self):
|
||||||
return self._height
|
return self._display.height
|
||||||
@height.setter
|
@height.setter
|
||||||
def height(self, height):
|
def height(self, height):
|
||||||
self._height = height
|
self._display.height = height
|
||||||
|
|
||||||
@pyqtProperty(int, constant=True)
|
@pyqtProperty(int, constant=True)
|
||||||
def x_offset(self):
|
def x_offset(self):
|
||||||
return self._x_offset
|
return self._display.x_offset
|
||||||
@x_offset.setter
|
@x_offset.setter
|
||||||
def x_offset(self, x_offset):
|
def x_offset(self, x_offset):
|
||||||
self._x_offset = x_offset
|
self._display.x_offset = x_offset
|
||||||
|
|
||||||
@pyqtProperty(int, constant=True)
|
@pyqtProperty(int, constant=True)
|
||||||
def y_offset(self):
|
def y_offset(self):
|
||||||
return self._y_offset
|
return self._display.y_offset
|
||||||
@y_offset.setter
|
@y_offset.setter
|
||||||
def y_offset(self, y_offset):
|
def y_offset(self, y_offset):
|
||||||
self._y_offset = y_offset
|
self._display.y_offset = y_offset
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Screen adjustment class
|
# Screen adjustment class
|
||||||
|
@ -224,9 +235,9 @@ class XRandR(SubprocessWrapper):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(XRandR, self).__init__()
|
super(XRandR, self).__init__()
|
||||||
self.mode_name: str
|
self.mode_name: str
|
||||||
self.screens: List[DisplayProperty] = []
|
self.screens: List[Display] = []
|
||||||
self.virt: DisplayProperty() = None
|
self.virt: Display() = None
|
||||||
self.primary: DisplayProperty() = None
|
self.primary: Display() = None
|
||||||
self.virt_idx: int = None
|
self.virt_idx: int = None
|
||||||
self.primary_idx: int = None
|
self.primary_idx: int = None
|
||||||
# Primary display
|
# Primary display
|
||||||
|
@ -241,7 +252,7 @@ class XRandR(SubprocessWrapper):
|
||||||
pattern = re.compile(r"^(\S*)\s+(connected|disconnected)\s+((primary)\s+)?"
|
pattern = re.compile(r"^(\S*)\s+(connected|disconnected)\s+((primary)\s+)?"
|
||||||
r"((\d+)x(\d+)\+(\d+)\+(\d+)\s+)?.*$", re.M)
|
r"((\d+)x(\d+)\+(\d+)\+(\d+)\s+)?.*$", re.M)
|
||||||
for idx, match in enumerate(pattern.finditer(output)):
|
for idx, match in enumerate(pattern.finditer(output)):
|
||||||
screen = DisplayProperty()
|
screen = Display()
|
||||||
screen.name = match.group(1)
|
screen.name = match.group(1)
|
||||||
if (self.virt_idx is None) and (screen.name == self.DEFAULT_VIRT_SCREEN):
|
if (self.virt_idx is None) and (screen.name == self.DEFAULT_VIRT_SCREEN):
|
||||||
self.virt_idx = idx
|
self.virt_idx = idx
|
||||||
|
@ -304,11 +315,11 @@ class XRandR(SubprocessWrapper):
|
||||||
self.delete_virtual_screen()
|
self.delete_virtual_screen()
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
||||||
def get_primary_screen(self) -> DisplayProperty:
|
def get_primary_screen(self) -> Display:
|
||||||
self._update_screens()
|
self._update_screens()
|
||||||
return self.primary
|
return self.primary
|
||||||
|
|
||||||
def get_virtual_screen(self) -> DisplayProperty:
|
def get_virtual_screen(self) -> Display:
|
||||||
self._update_screens()
|
self._update_screens()
|
||||||
return self.virt
|
return self.virt
|
||||||
|
|
||||||
|
@ -345,7 +356,8 @@ class Backend(QObject):
|
||||||
Q_ENUMS(VNCState)
|
Q_ENUMS(VNCState)
|
||||||
# Virtual screen properties
|
# Virtual screen properties
|
||||||
xrandr: XRandR
|
xrandr: XRandR
|
||||||
_virt: DisplayProperty = DisplayProperty()
|
_virt: Display = Display()
|
||||||
|
_virtProp: DisplayProperty
|
||||||
_portrait: bool
|
_portrait: bool
|
||||||
_hidpi: bool
|
_hidpi: bool
|
||||||
_virtScreenCreated: bool = False
|
_virtScreenCreated: bool = False
|
||||||
|
@ -357,7 +369,7 @@ class Backend(QObject):
|
||||||
_vncState: VNCState
|
_vncState: VNCState
|
||||||
_vncAutoStart: bool
|
_vncAutoStart: bool
|
||||||
# Primary screen and mouse posistion
|
# Primary screen and mouse posistion
|
||||||
primary: DisplayProperty()
|
_primaryProp: DisplayProperty
|
||||||
cursor_x: int
|
cursor_x: int
|
||||||
cursor_y: int
|
cursor_y: int
|
||||||
vncServer: ProcessProtocol
|
vncServer: ProcessProtocol
|
||||||
|
@ -377,8 +389,8 @@ class Backend(QObject):
|
||||||
try:
|
try:
|
||||||
with open(CONFIG_PATH, "r") as f:
|
with open(CONFIG_PATH, "r") as f:
|
||||||
settings = json.load(f)
|
settings = json.load(f)
|
||||||
self.virt.width = settings['virt']['width']
|
self._virt.width = settings['virt']['width']
|
||||||
self.virt.height = settings['virt']['height']
|
self._virt.height = settings['virt']['height']
|
||||||
self._portrait = settings['virt']['portrait']
|
self._portrait = settings['virt']['portrait']
|
||||||
self._hidpi = settings['virt']['hidpi']
|
self._hidpi = settings['virt']['hidpi']
|
||||||
self._vncPort = settings['vnc']['port']
|
self._vncPort = settings['vnc']['port']
|
||||||
|
@ -387,13 +399,14 @@ class Backend(QObject):
|
||||||
print("Default Setting used.")
|
print("Default Setting used.")
|
||||||
with open(DEFAULT_CONFIG_PATH, "r") as f:
|
with open(DEFAULT_CONFIG_PATH, "r") as f:
|
||||||
settings = json.load(f)
|
settings = json.load(f)
|
||||||
self.virt.width = settings['virt']['width']
|
self._virt.width = settings['virt']['width']
|
||||||
self.virt.height = settings['virt']['height']
|
self._virt.height = settings['virt']['height']
|
||||||
self._portrait = settings['virt']['portrait']
|
self._portrait = settings['virt']['portrait']
|
||||||
self._hidpi = settings['virt']['hidpi']
|
self._hidpi = settings['virt']['hidpi']
|
||||||
self._vncPort = settings['vnc']['port']
|
self._vncPort = settings['vnc']['port']
|
||||||
self._vncAutoStart = settings['vnc']['autostart']
|
self._vncAutoStart = settings['vnc']['autostart']
|
||||||
# create objects
|
# create objects
|
||||||
|
self._virtProp = DisplayProperty(self._virt)
|
||||||
self._vncState = self.VNCState.OFF
|
self._vncState = self.VNCState.OFF
|
||||||
self.xrandr = XRandR()
|
self.xrandr = XRandR()
|
||||||
self._virtScreenIndex = self.xrandr.virt_idx
|
self._virtScreenIndex = self.xrandr.virt_idx
|
||||||
|
@ -401,10 +414,10 @@ class Backend(QObject):
|
||||||
# Qt properties
|
# Qt properties
|
||||||
@pyqtProperty(DisplayProperty)
|
@pyqtProperty(DisplayProperty)
|
||||||
def virt(self):
|
def virt(self):
|
||||||
return self._virt
|
return self._virtProp
|
||||||
@virt.setter
|
@virt.setter
|
||||||
def virt(self, virt):
|
def virt(self, virt):
|
||||||
self._virt = virt
|
self._virtProp = virt
|
||||||
|
|
||||||
@pyqtProperty(bool)
|
@pyqtProperty(bool)
|
||||||
def portrait(self):
|
def portrait(self):
|
||||||
|
@ -430,7 +443,7 @@ class Backend(QObject):
|
||||||
|
|
||||||
@pyqtProperty(QQmlListProperty)
|
@pyqtProperty(QQmlListProperty)
|
||||||
def screens(self):
|
def screens(self):
|
||||||
return QQmlListProperty(DisplayProperty, self, self.xrandr.screens)
|
return QQmlListProperty(DisplayProperty, self, [DisplayProperty(x) for x in self.xrandr.screens])
|
||||||
|
|
||||||
@pyqtProperty(int, notify=onVirtScreenIndexChanged)
|
@pyqtProperty(int, notify=onVirtScreenIndexChanged)
|
||||||
def virtScreenIndex(self):
|
def virtScreenIndex(self):
|
||||||
|
@ -492,7 +505,8 @@ class Backend(QObject):
|
||||||
|
|
||||||
@pyqtProperty(DisplayProperty)
|
@pyqtProperty(DisplayProperty)
|
||||||
def primary(self):
|
def primary(self):
|
||||||
return self.xrandr.get_primary_screen()
|
self._primaryProp = DisplayProperty(self.xrandr.get_primary_screen())
|
||||||
|
return self._primaryProp
|
||||||
|
|
||||||
@pyqtProperty(int)
|
@pyqtProperty(int)
|
||||||
def cursor_x(self):
|
def cursor_x(self):
|
||||||
|
@ -508,7 +522,7 @@ class Backend(QObject):
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def createVirtScreen(self):
|
def createVirtScreen(self):
|
||||||
print("Creating a Virtual Screen...")
|
print("Creating a Virtual Screen...")
|
||||||
self.xrandr.create_virtual_screen(self.virt.width, self.virt.height, self.portrait, self.hidpi)
|
self.xrandr.create_virtual_screen(self._virt.width, self._virt.height, self.portrait, self.hidpi)
|
||||||
self.virtScreenCreated = True
|
self.virtScreenCreated = True
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
|
@ -613,8 +627,8 @@ class Backend(QObject):
|
||||||
with open(CONFIG_PATH, 'w') as f:
|
with open(CONFIG_PATH, 'w') as f:
|
||||||
settings = {}
|
settings = {}
|
||||||
settings['virt'] = {}
|
settings['virt'] = {}
|
||||||
settings['virt']['width'] = self.virt.width
|
settings['virt']['width'] = self._virt.width
|
||||||
settings['virt']['height'] = self.virt.height
|
settings['virt']['height'] = self._virt.height
|
||||||
settings['virt']['portrait'] = self._portrait
|
settings['virt']['portrait'] = self._portrait
|
||||||
settings['virt']['hidpi'] = self._hidpi
|
settings['virt']['hidpi'] = self._hidpi
|
||||||
settings['vnc'] = {}
|
settings['vnc'] = {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue