diff --git a/main.qml b/main.qml
index 716d09c..f45ce88 100644
--- a/main.qml
+++ b/main.qml
@@ -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
as a secondary monitor.
"
- }
- Text {
- text: "- Project Website"
- onLinkActivated: Qt.openUrlExternally(link)
- }
- Text {
- text: "- Issues & Bug Report"
- onLinkActivated: Qt.openUrlExternally(link)
- }
- Text {
- font { pointSize: 10 }
- anchors.horizontalCenter: parent.horizontalCenter
- horizontalAlignment: Text.AlignHCenter
- lineHeight: 0.7
- text: "
Copyright © 2018 Bumsik Kim Homepage
"
- onLinkActivated: Qt.openUrlExternally(link)
- }
- Text {
- font { pointSize: 9 }
- anchors.horizontalCenter: parent.horizontalCenter
- horizontalAlignment: Text.AlignHCenter
- text: "This program comes with absolutely no warranty.
" +
- "See the " +
- "GNU General Public License, version 3 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);
+ }
+}
\ No newline at end of file
diff --git a/mainWindow.qml b/mainWindow.qml
new file mode 100644
index 0000000..322f59f
--- /dev/null
+++ b/mainWindow.qml
@@ -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
as a secondary monitor.
"
+ }
+ Text {
+ text: "- Project Website"
+ onLinkActivated: Qt.openUrlExternally(link)
+ }
+ Text {
+ text: "- Issues & Bug Report"
+ onLinkActivated: Qt.openUrlExternally(link)
+ }
+ Text {
+ font { pointSize: 10 }
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ lineHeight: 0.7
+ text: "
Copyright © 2018 Bumsik Kim Homepage
"
+ onLinkActivated: Qt.openUrlExternally(link)
+ }
+ Text {
+ font { pointSize: 9 }
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: "This program comes with absolutely no warranty.
" +
+ "See the " +
+ "GNU General Public License, version 3 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
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/virtscreen.py b/virtscreen.py
index e843226..f2b6a80 100755
--- a/virtscreen.py
+++ b/virtscreen.py
@@ -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'] = {}