Skip to content

Commit

Permalink
Introduce DBus-powered x1plusd to manage settings (#223)
Browse files Browse the repository at this point in the history
Continuing in the path of #196, we introduce a handful of infrastructure in this PR, and only a few tiny users :-)  The core idea of this is that we introduce @riptidewave93 's `x1plusd` (and the associated `settings` service), but instead of binding it to DDS, we bind it to DBus using `jeepney` (which @jphannifan found after I spent a night unsuccessfully wrestling with the library dependencies for `dasbus` -- thanks!).  The `x1plusd` registers a DBus bus name `x1plus.x1plusd` that has an object `/x1plus/settings`, which, in turn, exposes the following methods:

* `x1plus.settings.GetSettings(json: string)` returns a JSON dictionary of all the current known X1Plus settings, loaded from the SD card's printer-specific X1Plus settings JSON.
* `x1plus.settings.PutSettings(json: string)` updates the X1Plus settings with keys from the passed JSON dictionary.

When a setting changes, it emits a signal:
* `x1plus.settings.SettingsChanged(json: string)`, with a dictionary that has set in it only the keys that have changed.

We also introduce a QML API, `X1Plus.Settings.get` / `X1Plus.Settings.put`.  `X1Plus.Settings.get` works like a QML binding, and if used in a QML binding context, will dynamically retrigger the binding if the setting changes either internally to `bbl_screen` or externally, as part of another system user's DBus operation.

Finally, we wire the lock screen's `locktype` and `passcode` variables to `X1Plus.Settings`, to store them in the `settings.json`, and we migrate these settings on boot if they exist.  To show the live QML binding behavior, we introduce a `x1plus-test-settings.py` that commands the X1Plus daemon to change some settings.  Test it by loading the lock screen configuration dialog, and then ssh'ing in and running something like:

`$ /opt/x1plus-test-settings.py passcode=1337`

or:

`$ /opt/x1plus-test-settings.py locktype=1`

You should see the settings change in real time on the display.

---------

Co-authored-by: Chris Blake <chrisrblake93@gmail.com>
  • Loading branch information
jwise and riptidewave93 committed Apr 29, 2024
1 parent 5e9f724 commit facd79e
Show file tree
Hide file tree
Showing 53 changed files with 4,794 additions and 37 deletions.
5 changes: 5 additions & 0 deletions bbl_screen-patch/patches/printerui/qml/X1Plus.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
.import "./x1plus/BedMeshCalibration.js" as X1PlusBedMeshCalibration
.import "./x1plus/ShaperCalibration.js" as X1PlusShaperCalibration
.import "./x1plus/DBus.js" as X1PlusDBus
.import "./x1plus/Settings.js" as X1PlusSettings

/* Back-end model logic for X1Plus's UI
*
Expand Down Expand Up @@ -51,13 +52,16 @@ X1Plus.GpioKeys = X1PlusGpioKeys;
var GpioKeys = X1PlusGpioKeys;
X1Plus.DBus = X1PlusDBus;
var DBus = X1PlusDBus;
X1Plus.Settings = X1PlusSettings;
var Settings = X1PlusSettings;

Stats.X1Plus = X1Plus;
DDS.X1Plus = X1Plus;
BedMeshCalibration.X1Plus = X1Plus;
ShaperCalibration.X1Plus = X1Plus;
GpioKeys.X1Plus = X1Plus;
DBus.X1Plus = X1Plus;
Settings.X1Plus = X1Plus;

var _DdsListener = JSDdsListener.DdsListener;
var _X1PlusNative = JSX1PlusNative.X1PlusNative;
Expand Down Expand Up @@ -143,6 +147,7 @@ function awaken(_DeviceManager, _PrintManager, _PrintTask) {
X1Plus.PrintTask = PrintTask = _PrintTask;
X1Plus.printerConfigDir = printerConfigDir = `/mnt/sdcard/x1plus/printers/${X1Plus.DeviceManager.build.seriaNO}`;
_X1PlusNative.system("mkdir -p " + _X1PlusNative.getenv("EMULATION_WORKAROUNDS") + printerConfigDir);
Settings.awaken();
BedMeshCalibration.awaken();
ShaperCalibration.awaken();
GpioKeys.awaken();
Expand Down
14 changes: 5 additions & 9 deletions bbl_screen-patch/patches/printerui/qml/settings/ScreenLock.qml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import UIBase 1.0
import Printer 1.0
import X1PlusNative 1.0

import "../X1Plus.js" as X1Plus
import "qrc:/uibase/qml/widgets"
import ".."

Rectangle {
property var locked: false
property var isEnteringPasscode: false
property var passcode: DeviceManager.getSetting("cfw_passcode", "")
property var locktype: DeviceManager.getSetting("cfw_locktype", 0)
property var passcode: X1Plus.Settings.get("passcode", "")
property var locktype: X1Plus.Settings.get("locktype", 0)
/* 0 = screensaver only, 1 = swipe to unlock, 2 = passcode */
property var lockImage: X1PlusNative.getenv("EMULATION_WORKAROUNDS") + DeviceManager.getSetting("cfw_lockscreen_image", '/mnt/sdcard/x1plus/lockscreen.png')
color: Colors.gray_800
Expand Down Expand Up @@ -48,7 +49,7 @@ Rectangle {


Component.onCompleted: {
if ((DeviceManager.getSetting("cfw_locktype", 0) == 2) && (DeviceManager.getSetting("cfw_passcode", "") != "")) {
if (locktype == 2 && passcode != "") {
/* lock on boot with a passcode */
didSleep();
locked = true;
Expand All @@ -61,7 +62,7 @@ Rectangle {
}

function didSleep() {
if (DeviceManager.getSetting("cfw_locktype", 0) != 0) {
if (locktype != 0) {
locked = true;
readText();
isEnteringPasscode = false;
Expand All @@ -71,11 +72,6 @@ Rectangle {
}
}

function refreshSettings() {
passcode = DeviceManager.getSetting("cfw_passcode", "");
locktype = DeviceManager.getSetting("cfw_locktype", 0);
}

NumberPad {
id: numberPad
anchors.top: parent.top
Expand Down
26 changes: 11 additions & 15 deletions bbl_screen-patch/patches/printerui/qml/settings/ScreenLockPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import UIBase 1.0
import Printer 1.0
import "../X1Plus.js" as X1Plus

import "qrc:/uibase/qml/widgets"
import ".."
import "../printer"

Item {
id: top
property var passcode: DeviceManager.getSetting("cfw_passcode", "")
Binding on passcode {
value: numberPad.number
when: numberPad.target == top
}
property var passcode: X1Plus.Settings.get("passcode", "")

MarginPanel {
id: title
Expand Down Expand Up @@ -177,13 +174,15 @@ Item {
backgroundColor: Colors.gray_500
width: 300
model: [qsTr("Screen saver only"), qsTr("Swipe to unlock"), qsTr("Passcode")]
currentIndex: DeviceManager.getSetting("cfw_locktype", 0)
onCurrentIndexChanged: {
DeviceManager.putSetting("cfw_locktype", currentIndex);
currentIndex: X1Plus.Settings.get("locktype", 0)
Binding on currentIndex {
value: X1Plus.Settings.get("locktype", 0)
}
onChoiseTapped: {
X1Plus.Settings.put("locktype", currentIndex);
const c = DeviceManager.power.mode; /* sort of janky mechanism to trigger the binding on the parent screen to reload */
DeviceManager.power.mode = 3 - c;
DeviceManager.power.mode = c;
screenLock.refreshSettings();
}
}

Expand Down Expand Up @@ -216,7 +215,7 @@ Item {
font: Fonts.body_30
color: passcode == "" && choiceMode.currentIndex == 2 && numberPad.target != top ? "#FF8080" : Colors.gray_100
text: numberPad.target == top
? qsTr('Passcode: "%1"').arg(passcode)
? qsTr('Passcode: "%1"').arg(numberPad.number)
: (passcode == ""
? qsTr("Passcode is unset")
: qsTr('Passcode set to "%1"').arg(passcode))
Expand Down Expand Up @@ -271,11 +270,8 @@ Item {
anchors.bottom: parent.bottom
focusItem: focus
onFinished: {
if (cancel) {
passcode = DeviceManager.getSetting("cfw_passcode", "")
} else {
DeviceManager.putSetting("cfw_passcode", number);
screenLock.refreshSettings();
if (!cancel) {
X1Plus.Settings.put("passcode", number);
}
}
}
Expand Down
71 changes: 71 additions & 0 deletions bbl_screen-patch/patches/printerui/qml/x1plus/Settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.pragma library

/* Core logic to mirror x1plusd settings to QML bindings
*
* Copyright (c) 2024 Joshua Wise, and the X1Plus authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var X1Plus = null;

var _PutSettings = null;
var _settings = {};
var _settingsBindings = {};

function _settingChanged(k, v) {
_settings[k] = v;
if (_settingsBindings[k]) {
_settingsBindings[k][2 /* set */](v);
}
}

function get(k, def) {
if (!_settingsBindings[k]) {
_settingsBindings[k] = X1Plus.Binding.makeBinding(_settings[k]);
}
var v = _settingsBindings[k][0 /* get */](); /* trigger the binding */
if (v === undefined || v === null) {
return def;
} else {
return v;
}
}

function put(k, v) {
_settingChanged(k, v);
_PutSettings({ [k]: v });
}

function _migrate(k, newk) {
var v = X1Plus.DeviceManager.getSetting(k, null);
if (v !== null) {
console.log(`X1Plus.Settings: migrating key ${k} -> ${newk}`);
put(newk, v);
DeviceManager.putSetting(k, null);
}
}

function awaken() {
X1Plus.DBus.onSignal("x1plus.settings", "SettingsChanged", (arg) => {
for (const k in arg) {
console.log(`x1plus.settings.SettingsChanged(${k})`);
_settingChanged(k, arg[k]);
}
});
_settings = X1Plus.DBus.proxyFunction("x1plus.x1plusd", "/x1plus/settings", "x1plus.settings", "GetSettings")();
_PutSettings = X1Plus.DBus.proxyFunction("x1plus.x1plusd", "/x1plus/settings", "x1plus.settings", "PutSettings");

_migrate("cfw_passcode", "passcode");
_migrate("cfw_locktype", "locktype");
}
7 changes: 4 additions & 3 deletions bbl_screen-patch/patches/root.qrc.patch
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<file>printerui/qml/settings/WifiListPage.qml</file>
<file>printerui/qml/settings/WifiSetupPage.qml</file>
<file>printerui/qml/wizard/Calibrate1Page.qml</file>
@@ -235,12 +283,26 @@
@@ -235,12 +283,27 @@
<file>printerui/qml/wizard/TermsPage.qml</file>
<file>printerui/qml/wizard/WelcomePage.qml</file>
<file>printerui/qml/wizard/WizardPage.qml</file>
Expand All @@ -127,6 +127,7 @@
+<file>printerui/qml/x1plus/GcodeGenerator.js</file>
+<file>printerui/qml/x1plus/GpioKeys.js</file>
+<file>printerui/qml/x1plus/MeshCalcs.js</file>
+<file>printerui/qml/x1plus/Settings.js</file>
+<file>printerui/qml/x1plus/ShaperCalibration.js</file>
+<file>printerui/qml/x1plus/Stats.js</file>
<file>printerui/text/error_texts.de.json</file>
Expand All @@ -139,15 +140,15 @@
<file>printerui/text/error_texts.sv.json</file>
<file>printerui/text/error_texts.zh-cn.json</file>
<file>printerui/text/error_texts.zh-tw.json</file>
@@ -259,6 +321,7 @@
@@ -259,6 +322,7 @@
<file>printerui/text/hms_texts.fr.json</file>
<file>printerui/text/hms_texts.ja.json</file>
<file>printerui/text/hms_texts.nl.json</file>
+<file>printerui/text/hms_texts.ru.json</file>
<file>printerui/text/hms_texts.sv.json</file>
<file>printerui/text/hms_texts.zh-cn.json</file>
<file>printerui/text/hms_texts.zh-tw.json</file>
@@ -295,3 +358,4 @@
@@ -295,3 +359,4 @@
<file>printerui/text/terms.zh-cn.txt</file>
<file>qt-project.org/imports/QtQuick/VirtualKeyboard/Styles/bbl/style.qml</file>
</qresource></RCC>
Expand Down
1 change: 0 additions & 1 deletion images/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/*.squashfs
cfw/opt/python/
27 changes: 18 additions & 9 deletions images/cfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
bio = io.BytesIO()

tf = TarFile(fileobj = bio, mode = 'w', format = tarfile.GNU_FORMAT)
packed = []

def addfile_once(ti, fileobj = None):
if ti.name in packed:
return
tf.addfile(ti, fileobj)
packed.append(ti.name)

def dir(dirname):
ti = TarInfo(dirname)
Expand All @@ -29,7 +36,7 @@ def dir(dirname):
ti.uid = 0
ti.gid = 0
ti.mtime = mtime
tf.addfile(ti)
addfile_once(ti)

def whiteout(path):
ti = TarInfo(path)
Expand All @@ -40,7 +47,7 @@ def whiteout(path):
ti.uid = 0
ti.gid = 0
ti.mtime = mtime
tf.addfile(ti)
addfile_once(ti)

def symlink(src, dest):
ti = TarInfo(src)
Expand All @@ -50,26 +57,27 @@ def symlink(src, dest):
ti.uid = 0
ti.gid = 0
ti.mtime = mtime
tf.addfile(ti)
addfile_once(ti)

def globfiles(fromdir, todir, eatlinks = []):
for f in glob.glob('**', root_dir = fromdir, recursive = True):
if f[-1] == "~":
continue
inf = f"{fromdir}/{f}"
ti = tf.gettarinfo(name = inf, arcname = f"{todir}/{f}")
ti = tf.gettarinfo(name = inf)
if (eatlinks == True or (f in eatlinks)) and ti.type == tarfile.SYMTYPE:
ti = tf.gettarinfo(name = os.path.join(os.path.dirname(inf), os.readlink(inf)), arcname = f"{todir}/{f}")
ti = tf.gettarinfo(name = os.path.join(os.path.dirname(inf), os.readlink(inf)))
ti.name = f"{todir}/{f}"
ti.uid = 0
ti.gid = 0
ti.uname = "root"
ti.gname = "root"
ti.mtime = mtime
if ti.isfile():
with open(inf, 'rb') as infd:
tf.addfile(ti, fileobj = infd)
addfile_once(ti, fileobj = infd)
else:
tf.addfile(ti)
addfile_once(ti)

symlink('/root/.ssh', '/config/sshd')
symlink('/etc/localtime', '/usr/share/zoneinfo/Etc/UTC')
Expand Down Expand Up @@ -106,6 +114,7 @@ def globfiles(fromdir, todir, eatlinks = []):
globfiles("cfw/sbin", "/sbin")
globfiles("cfw/system", "/system", eatlinks = True)
globfiles("cfw/opt", "/opt", eatlinks = True)
globfiles("site-packages", "/opt/python/lib/python3.10/site-packages")

with tarfile.open('../prebuilt/python3.tar.gz', 'r') as itf:
for ti in itf.getmembers():
Expand All @@ -120,9 +129,9 @@ def globfiles(fromdir, todir, eatlinks = []):
ti.gid = 0
ti.mtime = mtime
if ti.isfile():
tf.addfile(ti, itf.extractfile(ti))
addfile_once(ti, itf.extractfile(ti))
else:
tf.addfile(ti)
addfile_once(ti)

tf.close()
sys.stdout.buffer.write(bio.getbuffer())
36 changes: 36 additions & 0 deletions images/cfw/etc/init.d/S72x1plusd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh

# X1Plus Daemon, our Python do-it-all script that interfaces with DDS.

start() {
printf "Starting x1plusd: "
start-stop-daemon -S -m -b -p /var/run/x1plusd.pid --exec /opt/python/bin/python3 -- /opt/x1plusd.py
[ $? = 0 ] && echo "OK" || echo "FAIL"
}
stop() {
printf "Stopping x1plusd: "
start-stop-daemon -K -q -p /var/run/x1plusd.pid
[ $? = 0 ] && echo "OK" || echo "FAIL"
}
restart() {
stop
sleep 5
start
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac

exit $?

0 comments on commit facd79e

Please sign in to comment.