metadata.json claimed MIT while no LICENSE file existed, so the repository had no effective licence at all. It is now GPL-3.0-or-later: the full FSF text in LICENSE, the identifier in metadata.json, and REUSE-style SPDX-FileCopyrightText and SPDX-License-Identifier headers on all 33 source files, which is the convention KDE itself uses. Worth recording that GPL-3.0 is a preference here rather than an obligation. Nothing in the dependency graph forces it: linkctl is MIT and is invoked as a subprocess rather than linked, and the KDE QML modules the applet imports are LGPL. Removed two things that should not ship in a public repository. tests's QML linter had an absolute sandbox path, /tmp/pyside/PySide6/qmllint, baked into its search list; it now looks for Qt's qmllint and PySide6's pyside6-qmllint on PATH and honours a QMLLINT override, and says how to install one when it finds nothing. The systemd unit carried a Documentation= URL pointing at a host that does not exist, and its ExecStart silently assumed a particular checkout directory; the URL is gone and the path is now an obvious PATH/TO/CHECKOUT placeholder next to the comment telling you to edit it. Added a Credits section naming the work this is built on, since the project is mostly glue: linkctl does the camera control, schlarpc's firmware teardown documents the extension-unit protocol, and vrwallace's controller established that the Link 2 needs both pan and tilt axes in a single VIDIOC_S_EXT_CTRLS call, which is the detail the whole thing depends on. Also made the two Python tests executable, matching the other scripts. Audited for operator-specific detail: no credentials, tokens, private hostnames, IP addresses or absolute home paths in any tracked file or anywhere in the history's diffs. Three merge commits do carry Reviewed-on and Reviewed-by trailers naming a private forge, added by the forge at merge time; those are commit metadata rather than file content and are handled separately. Tested: 169 checks across seven suites plus the QML linter, all passing after the header insertion, which confirms it did not disturb the order-sensitive `.pragma library` line in logic.js or the qmldir module definitions. shellcheck clean.
91 lines
2.5 KiB
QML
91 lines
2.5 KiB
QML
// SPDX-FileCopyrightText: 2026 branderson
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls as QQC2
|
|
import org.kde.kirigami as Kirigami
|
|
|
|
Kirigami.FormLayout {
|
|
id: page
|
|
|
|
// The cfg_* aliases are the contract with Plasma's config system: each
|
|
// name must match an entry in config/main.xml.
|
|
property alias cfg_linkctlPath: linkctlPath.text
|
|
property alias cfg_device: device.text
|
|
property alias cfg_homePreset: homePreset.text
|
|
property alias cfg_maxStep: maxStep.value
|
|
property alias cfg_tickInterval: tickInterval.value
|
|
property alias cfg_deadzone: deadzone.value
|
|
property alias cfg_invertPan: invertPan.checked
|
|
property alias cfg_invertTilt: invertTilt.checked
|
|
|
|
// Plasma also assigns a cfg_<name>Default for every key, plus a title,
|
|
// when it instantiates a config page. Without these declared it logs
|
|
// "does not have a property called cfg_<name>Default" for each one and
|
|
// the Defaults button has nothing to compare against.
|
|
property string cfg_linkctlPathDefault: "linkctl"
|
|
property string cfg_deviceDefault: ""
|
|
property string cfg_homePresetDefault: "home"
|
|
property int cfg_maxStepDefault: 6
|
|
property int cfg_tickIntervalDefault: 160
|
|
property int cfg_deadzoneDefault: 15
|
|
property bool cfg_invertPanDefault: false
|
|
property bool cfg_invertTiltDefault: false
|
|
property string title: ""
|
|
|
|
QQC2.TextField {
|
|
id: linkctlPath
|
|
Kirigami.FormData.label: "linkctl binary:"
|
|
placeholderText: "linkctl"
|
|
}
|
|
|
|
QQC2.TextField {
|
|
id: device
|
|
Kirigami.FormData.label: "Video device:"
|
|
placeholderText: "auto-detect"
|
|
}
|
|
|
|
QQC2.TextField {
|
|
id: homePreset
|
|
Kirigami.FormData.label: "Home preset:"
|
|
placeholderText: "home"
|
|
}
|
|
|
|
Item {
|
|
Kirigami.FormData.isSection: true
|
|
}
|
|
|
|
QQC2.SpinBox {
|
|
id: maxStep
|
|
Kirigami.FormData.label: "Degrees per step:"
|
|
from: 1
|
|
to: 30
|
|
}
|
|
|
|
QQC2.SpinBox {
|
|
id: tickInterval
|
|
Kirigami.FormData.label: "Step interval (ms):"
|
|
from: 60
|
|
to: 1000
|
|
stepSize: 20
|
|
}
|
|
|
|
QQC2.SpinBox {
|
|
id: deadzone
|
|
Kirigami.FormData.label: "Deadzone (%):"
|
|
from: 0
|
|
to: 50
|
|
}
|
|
|
|
QQC2.CheckBox {
|
|
id: invertPan
|
|
Kirigami.FormData.label: "Invert:"
|
|
text: "Horizontal axis"
|
|
}
|
|
|
|
QQC2.CheckBox {
|
|
id: invertTilt
|
|
text: "Vertical axis"
|
|
}
|
|
}
|