1
0
Fork 0
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:
Bumsik Kim 2018-05-13 17:45:03 -04:00
parent 863cb99124
commit fe9b3fb13e
No known key found for this signature in database
GPG key ID: E31041C8EC5B01C6
3 changed files with 621 additions and 641 deletions

689
main.qml
View file

@ -1,70 +1,24 @@
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.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;
}
}
// One-shot signal connect
function connectOnce (signal, slot) {
var f = function() {
slot.apply(this, arguments);
signal.disconnect(f);
}
signal.connect(f);
}
Loader {
id: mainLoader
active: false
source: "mainWindow.qml"
property alias window: mainLoader.item
// virtscreen.py backend.
Backend {
id: backend
function switchVNC () {
if ((backend.vncState == Backend.OFF) && backend.virtScreenCreated) {
backend.startVNC();
}
}
onVncAutoStartChanged: {
if (vncAutoStart) {
onVirtScreenCreatedChanged.connect(switchVNC);
@ -74,14 +28,12 @@ ApplicationWindow {
onVncStateChanged.disconnect(switchVNC);
}
}
Component.onCompleted: {
// force emit signal on load
vncAutoStart = vncAutoStart;
}
}
// Timer object and function
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);
}
// Sytray Icon
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);
}
menuBar: ToolBar {
id: toolbar
font.weight: Font.Medium
font.pointSize: 11 //parent.font.pointSize + 1
onActivated: function(reason) {
console.log(reason);
if (reason == SystemTrayIcon.Context) {
return;
}
sysTrayIcon.clicked = true;
mainLoader.active = true;
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: margin + 10
Label {
id: vncStateLabel
menu: Menu {
MenuItem {
id: vncStateText
enabled: false
text: !backend.virtScreenCreated ? "Enable Virtual Screen first." :
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.CONNECTED ? "Connected." :
"Server state error!"
}
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();
}
}
}
MenuItem {
separator: true
}
}
}
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
MenuItem {
id: virtScreenAction
text: backend.virtScreenCreated ? "Disable Virtual Screen" : "Enable Virtual Screen"
highlighted: true
anchors.left: parent.left
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();
enabled:backend.vncAutoStart ? true :
backend.vncState == Backend.OFF ? true : false
onTriggered: {
// Give a very short delay to show busyDialog.
timer.setTimeout (function() {
if (!backend.virtScreenCreated) {
@ -417,263 +127,19 @@ ApplicationWindow {
}
}, 200);
}
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
MenuItem {
id: vncAction
text: backend.vncAutoStart ? "Auto start enabled" :
backend.vncState == Backend.OFF ? "Start VNC Server" : "Stop VNC Server"
enabled: backend.vncAutoStart ? false :
backend.virtScreenCreated ? true : false
// Material.background: Material.Teal
// Material.foreground: Material.Grey
onClicked: backend.vncState == Backend.OFF ? backend.startVNC() : backend.stopVNC()
onTriggered: backend.vncState == Backend.OFF ? backend.startVNC() : backend.stopVNC()
}
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 {
MenuItem {
separator: true
}
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 {
MenuItem {
text: qsTr("&Quit")
onTriggered: {
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
View 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
}
}
}
}
}
}
}

View file

@ -131,18 +131,17 @@ class ProcessProtocol(protocol.ProcessProtocol):
#-------------------------------------------------------------------------------
# Display properties
#-------------------------------------------------------------------------------
class DisplayProperty(QObject):
_name: str
_primary: bool
_connected: bool
_active: bool
_width: int
_height: int
_x_offset: int
_y_offset: int
class Display(object):
__slots__ = ['name', 'primary', 'connected', 'active', 'width', 'height', 'x_offset', 'y_offset']
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):
ret = f"{self.name}"
@ -155,64 +154,76 @@ class DisplayProperty(QObject):
if self.active:
ret += f" {self.width}x{self.height}+{self.x_offset}+{self.y_offset}"
else:
ret += " not active"
ret += f" not active {self.width}x{self.height}"
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)
def name(self):
return self._name
return self._display.name
@name.setter
def name(self, name):
self._name = name
self._display.name = name
@pyqtProperty(bool, constant=True)
def primary(self):
return self._primary
return self._display.primary
@primary.setter
def primary(self, primary):
self._primary = primary
self._display.primary = primary
@pyqtProperty(bool, constant=True)
def connected(self):
return self._connected
return self._display.connected
@connected.setter
def connected(self, connected):
self._connected = connected
self._display.connected = connected
@pyqtProperty(bool, constant=True)
def active(self):
return self._active
return self._display.active
@active.setter
def active(self, active):
self._active = active
self._display.active = active
@pyqtProperty(int, constant=True)
def width(self):
return self._width
return self._display.width
@width.setter
def width(self, width):
self._width = width
self._display.width = width
@pyqtProperty(int, constant=True)
def height(self):
return self._height
return self._display.height
@height.setter
def height(self, height):
self._height = height
self._display.height = height
@pyqtProperty(int, constant=True)
def x_offset(self):
return self._x_offset
return self._display.x_offset
@x_offset.setter
def x_offset(self, x_offset):
self._x_offset = x_offset
self._display.x_offset = x_offset
@pyqtProperty(int, constant=True)
def y_offset(self):
return self._y_offset
return self._display.y_offset
@y_offset.setter
def y_offset(self, y_offset):
self._y_offset = y_offset
self._display.y_offset = y_offset
#-------------------------------------------------------------------------------
# Screen adjustment class
@ -224,9 +235,9 @@ class XRandR(SubprocessWrapper):
def __init__(self):
super(XRandR, self).__init__()
self.mode_name: str
self.screens: List[DisplayProperty] = []
self.virt: DisplayProperty() = None
self.primary: DisplayProperty() = None
self.screens: List[Display] = []
self.virt: Display() = None
self.primary: Display() = None
self.virt_idx: int = None
self.primary_idx: int = None
# Primary display
@ -241,7 +252,7 @@ class XRandR(SubprocessWrapper):
pattern = re.compile(r"^(\S*)\s+(connected|disconnected)\s+((primary)\s+)?"
r"((\d+)x(\d+)\+(\d+)\+(\d+)\s+)?.*$", re.M)
for idx, match in enumerate(pattern.finditer(output)):
screen = DisplayProperty()
screen = Display()
screen.name = match.group(1)
if (self.virt_idx is None) and (screen.name == self.DEFAULT_VIRT_SCREEN):
self.virt_idx = idx
@ -304,11 +315,11 @@ class XRandR(SubprocessWrapper):
self.delete_virtual_screen()
os._exit(0)
def get_primary_screen(self) -> DisplayProperty:
def get_primary_screen(self) -> Display:
self._update_screens()
return self.primary
def get_virtual_screen(self) -> DisplayProperty:
def get_virtual_screen(self) -> Display:
self._update_screens()
return self.virt
@ -345,7 +356,8 @@ class Backend(QObject):
Q_ENUMS(VNCState)
# Virtual screen properties
xrandr: XRandR
_virt: DisplayProperty = DisplayProperty()
_virt: Display = Display()
_virtProp: DisplayProperty
_portrait: bool
_hidpi: bool
_virtScreenCreated: bool = False
@ -357,7 +369,7 @@ class Backend(QObject):
_vncState: VNCState
_vncAutoStart: bool
# Primary screen and mouse posistion
primary: DisplayProperty()
_primaryProp: DisplayProperty
cursor_x: int
cursor_y: int
vncServer: ProcessProtocol
@ -377,8 +389,8 @@ class Backend(QObject):
try:
with open(CONFIG_PATH, "r") as f:
settings = json.load(f)
self.virt.width = settings['virt']['width']
self.virt.height = settings['virt']['height']
self._virt.width = settings['virt']['width']
self._virt.height = settings['virt']['height']
self._portrait = settings['virt']['portrait']
self._hidpi = settings['virt']['hidpi']
self._vncPort = settings['vnc']['port']
@ -387,13 +399,14 @@ class Backend(QObject):
print("Default Setting used.")
with open(DEFAULT_CONFIG_PATH, "r") as f:
settings = json.load(f)
self.virt.width = settings['virt']['width']
self.virt.height = settings['virt']['height']
self._virt.width = settings['virt']['width']
self._virt.height = settings['virt']['height']
self._portrait = settings['virt']['portrait']
self._hidpi = settings['virt']['hidpi']
self._vncPort = settings['vnc']['port']
self._vncAutoStart = settings['vnc']['autostart']
# create objects
self._virtProp = DisplayProperty(self._virt)
self._vncState = self.VNCState.OFF
self.xrandr = XRandR()
self._virtScreenIndex = self.xrandr.virt_idx
@ -401,10 +414,10 @@ class Backend(QObject):
# Qt properties
@pyqtProperty(DisplayProperty)
def virt(self):
return self._virt
return self._virtProp
@virt.setter
def virt(self, virt):
self._virt = virt
self._virtProp = virt
@pyqtProperty(bool)
def portrait(self):
@ -430,7 +443,7 @@ class Backend(QObject):
@pyqtProperty(QQmlListProperty)
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)
def virtScreenIndex(self):
@ -492,7 +505,8 @@ class Backend(QObject):
@pyqtProperty(DisplayProperty)
def primary(self):
return self.xrandr.get_primary_screen()
self._primaryProp = DisplayProperty(self.xrandr.get_primary_screen())
return self._primaryProp
@pyqtProperty(int)
def cursor_x(self):
@ -508,7 +522,7 @@ class Backend(QObject):
@pyqtSlot()
def createVirtScreen(self):
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
@pyqtSlot()
@ -613,8 +627,8 @@ class Backend(QObject):
with open(CONFIG_PATH, 'w') as f:
settings = {}
settings['virt'] = {}
settings['virt']['width'] = self.virt.width
settings['virt']['height'] = self.virt.height
settings['virt']['width'] = self._virt.width
settings['virt']['height'] = self._virt.height
settings['virt']['portrait'] = self._portrait
settings['virt']['hidpi'] = self._hidpi
settings['vnc'] = {}