Skip to content

Commit 92a3011

Browse files
authored
Harden beta setup, diagnostics, and packaging (#9)
* feat(packaging): include mwb_tray in RPM package * Harden beta setup and diagnostics * Remove internal planning docs from beta hardening PR * Fix Fedora CI RPM build dependencies
1 parent c4d61d2 commit 92a3011

11 files changed

Lines changed: 892 additions & 10 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ jobs:
5959
dnf install -y \
6060
cmake \
6161
gcc-c++ \
62+
gtk3-devel \
63+
libayatana-appindicator3-devel \
6264
make \
6365
openssl-devel \
6466
pkgconf-pkg-config \
@@ -123,6 +125,7 @@ jobs:
123125
- name: Check shell syntax
124126
run: |
125127
bash -n mwb-desktop-ui.sh
128+
bash -n scripts/inputflow-diagnostics-bundle.sh
126129
bash -n scripts/validate-rpm-packaging.sh
127130
128131
- name: Validate RPM packaging skeleton

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Recommended first-run flow for most users:
2020
- Run the exported `.ps1` script on your Windows machine to register the Linux peer.
2121
5. **Start:** Choose **Start Service** or launch the tray with `./build/mwb_tray`.
2222

23+
For the full beta setup, health-check, diagnostics, connection-quality, and packaging-verification workflow, see [docs/beta-workflow.md](docs/beta-workflow.md).
24+
2325
---
2426

2527
## 🛠️ Build & Installation
@@ -93,6 +95,13 @@ For power users who prefer manual control:
9395

9496
See the full [documentation section](#detailed-documentation) for environment variables and protocol details.
9597

98+
User-facing beta operations:
99+
100+
- [Guided Windows pairing and export helper](docs/beta-workflow.md#guided-pairing-and-export-helper)
101+
- [Health checks and diagnostics bundle](docs/beta-workflow.md#health-check)
102+
- [Connection quality and latency reporting](docs/beta-workflow.md#connection-quality)
103+
- [Packaging verification](docs/beta-workflow.md#packaging-verification)
104+
96105
<a name="detailed-documentation"></a>
97106
## Detailed Documentation
98107

docs/beta-workflow.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# InputFlow Public Beta Workflow
2+
3+
This guide covers the user-facing beta path for pairing Linux with PowerToys Mouse Without Borders on Windows, checking health, collecting diagnostics, tuning connection quality, and validating package output.
4+
5+
## Guided Pairing And Export Helper
6+
7+
Use the desktop controller for the guided path:
8+
9+
```bash
10+
./mwb-desktop-ui.sh menu
11+
```
12+
13+
1. Open **Settings** and enter the Windows host IP, local machine name, port, and exactly one authentication source: inline key, `key_file`, or Secret Service key ID.
14+
2. Use **Connection Behavior** to choose automatic reconnect behavior before starting the service.
15+
3. Export a Windows helper from Linux when the UI exposes the action, or use the CLI fallback:
16+
17+
```bash
18+
./build/mwb_client export-windows-pair \
19+
--config ~/.config/mwb-client/config.ini \
20+
--output inputflow-windows-pair.ps1 \
21+
--position auto
22+
```
23+
24+
The helper writes the Linux peer name, IP, layout position, shared key, `MachinePool`, `MachineMatrixString`, and `Name2IP` values expected by PowerToys. On Windows, install and open PowerToys Mouse Without Borders once, then run the exported script in PowerShell:
25+
26+
```powershell
27+
powershell -ExecutionPolicy Bypass -File .\inputflow-windows-pair.ps1
28+
```
29+
30+
Keep the exported `.ps1` private because it contains pairing material. Delete it after confirming Windows sees the Linux peer.
31+
32+
![Pairing helper walkthrough](screenshots/pairing-helper.svg)
33+
34+
## Health Check
35+
36+
Run the built-in doctor before filing a beta issue or after changing package/service setup:
37+
38+
```bash
39+
./build/mwb_client doctor --config ~/.config/mwb-client/config.ini
40+
```
41+
42+
The desktop controller shows the same health check together with the user service status through:
43+
44+
```bash
45+
./mwb-desktop-ui.sh status
46+
```
47+
48+
Review warnings for missing `/dev/uinput` access, missing `inputflow` group membership, missing packaged files, unavailable clipboard helpers, or invalid authentication configuration.
49+
50+
## Diagnostics Bundle
51+
52+
Until a one-click diagnostics bundle command is available, collect this minimal bundle for beta reports:
53+
54+
```bash
55+
./build/mwb_client doctor --config ~/.config/mwb-client/config.ini > inputflow-doctor.txt
56+
systemctl --user status --no-pager mwb-client.service > inputflow-service-status.txt
57+
journalctl --user -u mwb-client.service --since "30 minutes ago" --no-pager > inputflow-service-log.txt
58+
```
59+
60+
Also include `~/.config/mwb-client/config.ini` with `key`, `key_file`, `key_secret_id`, Windows IPs, and hostnames redacted as needed. Do not attach exported Windows helper scripts or unredacted Secret Service identifiers to public issues.
61+
62+
## Connection Quality
63+
64+
For most beta users, keep automatic reconnect enabled and tune only if the service reconnects too aggressively or too slowly:
65+
66+
```ini
67+
auto_connect_enabled=true
68+
reconnect_initial_backoff_ms=500
69+
reconnect_max_backoff_ms=30000
70+
reconnect_idle_retry_ms=5000
71+
latency_report=false
72+
```
73+
74+
Enable latency reporting only while debugging missed or delayed input:
75+
76+
```bash
77+
./build/mwb_client run --config ~/.config/mwb-client/config.ini --latency-report
78+
```
79+
80+
You can also set `latency_report=true` in `config.ini` or run with `MWB_LATENCY_REPORT=1`. The report prints client-side queue and injection timing when the client shuts down.
81+
82+
## Packaging Verification
83+
84+
Validate RPM metadata without a full RPM build:
85+
86+
```bash
87+
scripts/validate-rpm-packaging.sh
88+
```
89+
90+
Run full Fedora RPM validation when the build dependencies are available:
91+
92+
```bash
93+
MWB_VALIDATE_RPM_BUILD=1 scripts/validate-rpm-packaging.sh
94+
```
95+
96+
The validation checks that the package keeps the compatibility binary and service names, includes the user unit, sysusers integration, modules-load file, and `/dev/uinput` udev rule, and does not introduce undocumented command aliases.
97+
98+
![Tray and controller workflow](screenshots/tray-controller.svg)

mwb-desktop-ui.sh

Lines changed: 183 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ RECONNECT_IDLE_CONFIG_KEY="${MWB_RECONNECT_IDLE_CONFIG_KEY:-reconnect_idle_retry
1313
MPRIS_MEDIA_KEYS_CONFIG_KEY="${MWB_MPRIS_MEDIA_KEYS_CONFIG_KEY:-mpris_media_keys_enabled}"
1414
MPRIS_PLAYER_CONFIG_KEY="${MWB_MPRIS_PLAYER_CONFIG_KEY:-mpris_player}"
1515
LATENCY_REPORT_CONFIG_KEY="${MWB_LATENCY_REPORT_CONFIG_KEY:-latency_report}"
16+
DIAGNOSTICS_BUNDLE_SCRIPT="$SCRIPT_DIR/scripts/inputflow-diagnostics-bundle.sh"
1617
DEFAULT_AUTO_CONNECT_ENABLED="${MWB_DEFAULT_AUTO_CONNECT_ENABLED:-true}"
1718
DEFAULT_RECONNECT_INITIAL_MS="${MWB_DEFAULT_RECONNECT_INITIAL_MS:-1000}"
1819
DEFAULT_RECONNECT_MAX_MS="${MWB_DEFAULT_RECONNECT_MAX_MS:-300000}"
@@ -680,14 +681,118 @@ menu_summary_text() {
680681
show_status() {
681682
local status_text doctor_text
682683
status_text="$(systemctl --user status --no-pager "$SERVICE_NAME" 2>&1 || true)"
683-
doctor_text="$("$APP_BIN" doctor --config "$CONFIG_PATH" 2>&1 || true)"
684+
doctor_text="$("$APP_BIN" doctor --config "$CONFIG_PATH" --state "$STATE_PATH" 2>&1 || true)"
684685
zenity --text-info --title="$APP_NAME service status" --width=900 --height=600 <<<"$doctor_text
685686
686687
----
687688
688689
$status_text"
689690
}
690691

692+
append_check_line() {
693+
local status="$1" name="$2" detail="$3"
694+
printf '%-5s %-28s %s\n' "$status" "$name" "$detail"
695+
}
696+
697+
probe_tcp_port() {
698+
local host="$1" port="$2"
699+
MWB_PROBE_HOST="$host" MWB_PROBE_PORT="$port" timeout 2 bash -c ':</dev/tcp/$MWB_PROBE_HOST/$MWB_PROBE_PORT' >/dev/null 2>&1
700+
}
701+
702+
health_check() {
703+
require_client_binary || return 1
704+
local host port key key_file secret_id auth_count service_status health_text doctor_text
705+
host="$(read_config_value host)"
706+
port="$(read_config_value port)"; [[ -n "$port" ]] || port="15101"
707+
key="$(read_config_value key)"
708+
key_file="$(read_config_value key_file)"
709+
secret_id="$(read_secret_id_value)"
710+
auth_count="$(configured_auth_source_count "$key" "$key_file" "$secret_id")"
711+
service_status="$(service_state)"
712+
713+
health_text="$(
714+
append_check_line "$([[ -f "$CONFIG_PATH" ]] && printf OK || printf WARN)" "config file" "$CONFIG_PATH"
715+
append_check_line "$([[ -e /dev/uinput ]] && printf OK || printf WARN)" "uinput device" "$([[ -e /dev/uinput ]] && ls -l /dev/uinput 2>/dev/null || printf 'missing; install udev rule and reload')"
716+
append_check_line "$([[ "$service_status" == "active" ]] && printf OK || printf WARN)" "user service" "$(service_state_label "$service_status")"
717+
append_check_line "$([[ -n "$host" ]] && printf OK || printf WARN)" "Windows host" "${host:-not configured}"
718+
append_check_line "$([[ "$auth_count" == "1" ]] && printf OK || printf WARN)" "authentication" "$(configured_auth_label "$key" "$key_file" "$secret_id")"
719+
if [[ -n "$host" ]] && command -v timeout >/dev/null 2>&1; then
720+
if probe_tcp_port "$host" "$port"; then
721+
append_check_line OK "input port" "$host:$port reachable"
722+
else
723+
append_check_line WARN "input port" "$host:$port not reachable"
724+
fi
725+
if probe_tcp_port "$host" "15100"; then
726+
append_check_line OK "clipboard port" "$host:15100 reachable"
727+
else
728+
append_check_line WARN "clipboard port" "$host:15100 not reachable"
729+
fi
730+
else
731+
append_check_line WARN "port probe" "host or timeout command unavailable"
732+
fi
733+
)"
734+
doctor_text="$("$APP_BIN" doctor --config "$CONFIG_PATH" --state "$STATE_PATH" 2>&1 || true)"
735+
zenity --text-info --title="$APP_NAME health check" --width=900 --height=620 <<<"$health_text
736+
737+
----
738+
Client doctor
739+
----
740+
$doctor_text"
741+
}
742+
743+
connection_quality() {
744+
local host port state auto_connect_enabled reconnect_initial_backoff_ms reconnect_max_backoff_ms reconnect_idle_retry_ms
745+
local clipboard_enabled clipboard_send_enabled clipboard_force_poll clipboard_poll_ms latency_report quality_text peer_lines
746+
host="$(read_config_value host)"
747+
port="$(read_config_value port)"; [[ -n "$port" ]] || port="15101"
748+
state="$(service_state)"
749+
IFS=$'\t' read -r auto_connect_enabled reconnect_initial_backoff_ms reconnect_max_backoff_ms reconnect_idle_retry_ms < <(read_connection_behavior_values)
750+
clipboard_enabled="$(read_config_value clipboard_enabled)"; [[ -n "$clipboard_enabled" ]] || clipboard_enabled="true"
751+
clipboard_send_enabled="$(read_config_value clipboard_send_enabled)"; [[ -n "$clipboard_send_enabled" ]] || clipboard_send_enabled="true"
752+
clipboard_force_poll="$(read_config_value clipboard_force_poll)"; [[ -n "$clipboard_force_poll" ]] || clipboard_force_poll="false"
753+
clipboard_poll_ms="$(read_config_value clipboard_poll_ms)"; [[ -n "$clipboard_poll_ms" ]] || clipboard_poll_ms="1000"
754+
latency_report="$(read_config_value "$LATENCY_REPORT_CONFIG_KEY")"; [[ -n "$latency_report" ]] || latency_report="false"
755+
756+
peer_lines="No peer state has been recorded yet."
757+
if [[ -f "$STATE_PATH" ]]; then
758+
peer_lines="$(awk -F'\t' '
759+
/^peer=/ {
760+
sub(/^peer=/, "", $1)
761+
name=$2; approved=$3; connected=$4; last_seen=$5; last_connected=$6
762+
if (name == "") name="unknown"
763+
printf "- %s (%s): paired=%s connected=%s last_seen=%s last_connected=%s\n", name, $1, approved, connected, last_seen, last_connected
764+
}
765+
' "$STATE_PATH")"
766+
[[ -n "$peer_lines" ]] || peer_lines="No peer entries found in $STATE_PATH."
767+
fi
768+
769+
quality_text="Service: $(service_state_label "$state") ($state)
770+
Configured host: ${host:-not configured}
771+
Input port: $port
772+
Clipboard port: 15100
773+
Reconnect mode: $( [[ "$auto_connect_enabled" == "true" ]] && printf 'Auto' || printf 'Manual' )
774+
Reconnect timing: initial=${reconnect_initial_backoff_ms}ms max=${reconnect_max_backoff_ms}ms idle=${reconnect_idle_retry_ms}ms
775+
Clipboard: enabled=$clipboard_enabled send_local=$clipboard_send_enabled force_poll=$clipboard_force_poll poll=${clipboard_poll_ms}ms
776+
Latency report logging: $latency_report
777+
778+
Known peers:
779+
$peer_lines"
780+
zenity --text-info --title="$APP_NAME connection quality" --width=840 --height=520 <<<"$quality_text"
781+
}
782+
783+
diagnostics_bundle() {
784+
if [[ ! -x "$DIAGNOSTICS_BUNDLE_SCRIPT" ]]; then
785+
zenity --error --text="Diagnostics bundle script is not available:
786+
$DIAGNOSTICS_BUNDLE_SCRIPT"
787+
return 1
788+
fi
789+
local output_dir result
790+
output_dir="$(zenity --file-selection --directory --title="$APP_NAME diagnostics output folder" || true)"
791+
[[ -n "$output_dir" ]] || return 1
792+
result="$("$DIAGNOSTICS_BUNDLE_SCRIPT" --config "$CONFIG_PATH" --state "$STATE_PATH" --output "$output_dir" 2>&1 || true)"
793+
zenity --text-info --title="$APP_NAME diagnostics bundle" --width=760 --height=420 <<<"$result"
794+
}
795+
691796
show_peers() {
692797
local rows=() configured_host selected_peer selected_host selected_port selected_name selected_action
693798
configured_host="$(read_config_value host)"
@@ -829,6 +934,53 @@ discover_and_save_peer() {
829934
fi
830935
}
831936

937+
export_windows_helper() {
938+
require_client_binary || return 1
939+
local output_dir position result
940+
output_dir="$(zenity --file-selection --directory --title="$APP_NAME Windows helper output folder" || true)"
941+
[[ -n "$output_dir" ]] || return 1
942+
position="$(zenity --list --radiolist --title="$APP_NAME Windows helper" --width=560 --height=260 \
943+
--text="Choose where the Linux desktop sits relative to the Windows host." \
944+
--column="Use" --column="Position" \
945+
TRUE "auto" \
946+
FALSE "top-left" \
947+
FALSE "top-right" \
948+
FALSE "bottom-left" \
949+
FALSE "bottom-right" || true)"
950+
[[ -n "$position" ]] || return 1
951+
result="$("$APP_BIN" export-windows-pair --config "$CONFIG_PATH" --output "$output_dir" --position "$position" --force 2>&1 || true)"
952+
zenity --text-info --title="$APP_NAME Windows pairing helper" --width=820 --height=440 <<<"$result
953+
954+
Next steps:
955+
1. Copy the exported .ps1 file to the Windows machine.
956+
2. Start PowerToys once so Mouse Without Borders creates its settings file.
957+
3. Run the helper in PowerShell.
958+
4. Return here and run Health Check."
959+
}
960+
961+
guided_pairing() {
962+
while true; do
963+
local choice
964+
choice="$(zenity --list --title="$APP_NAME guided pairing" --width=620 --height=360 \
965+
--text="Use this flow to discover Windows, save Linux settings, export the Windows helper, then verify the setup." \
966+
--column="Step" \
967+
"1. Discover Windows peer and save settings" \
968+
"2. Edit settings manually" \
969+
"3. Export Windows helper" \
970+
"4. Start service" \
971+
"5. Run health check" \
972+
"Back" || true)"
973+
case "$choice" in
974+
"1. Discover Windows peer and save settings") discover_and_save_peer ;;
975+
"2. Edit settings manually") edit_settings ;;
976+
"3. Export Windows helper") export_windows_helper ;;
977+
"4. Start service") start_session ;;
978+
"5. Run health check") health_check ;;
979+
""|"Back") return 0 ;;
980+
esac
981+
done
982+
}
983+
832984
edit_settings() {
833985
local preset_host="${1:-}"
834986
local host key key_file secret_id secret_key_name machine_name port screen_width screen_height auto_connect_enabled reconnect_initial_backoff_ms reconnect_max_backoff_ms reconnect_idle_retry_ms clipboard_enabled clipboard_force_poll clipboard_poll_ms
@@ -1045,7 +1197,23 @@ Terminal=false
10451197
Categories=Utility;Network;
10461198
Keywords=mouse;keyboard;sharing;input;controller;
10471199
StartupNotify=false
1048-
Actions=OpenSettings;OpenConnectionBehavior;ShowTrayHelp;ShowStatus;StartService;RestartService;StopService;
1200+
Actions=GuidedPairing;HealthCheck;DiagnosticsBundle;ConnectionQuality;OpenSettings;OpenConnectionBehavior;ShowTrayHelp;ShowStatus;StartService;RestartService;StopService;
1201+
1202+
[Desktop Action GuidedPairing]
1203+
Name=Guided Pairing
1204+
Exec=$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}") guided-pairing
1205+
1206+
[Desktop Action HealthCheck]
1207+
Name=Health Check
1208+
Exec=$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}") health-check
1209+
1210+
[Desktop Action DiagnosticsBundle]
1211+
Name=Diagnostics Bundle
1212+
Exec=$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}") diagnostics-bundle
1213+
1214+
[Desktop Action ConnectionQuality]
1215+
Name=Connection Quality
1216+
Exec=$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}") connection-quality
10491217
10501218
[Desktop Action OpenSettings]
10511219
Name=Open Settings
@@ -1102,6 +1270,10 @@ main_menu() {
11021270
local choice
11031271
choice="$(zenity --list --title="$APP_NAME" --text="$(menu_summary_text)" --width=540 --height=400 \
11041272
--column="Action" \
1273+
"Guided Pairing" \
1274+
"Health Check" \
1275+
"Diagnostics Bundle" \
1276+
"Connection Quality" \
11051277
"Settings" \
11061278
"Peers (Discovery & Known)" \
11071279
"Connection Behavior" \
@@ -1114,6 +1286,10 @@ main_menu() {
11141286
"Quit" || true)"
11151287

11161288
case "$choice" in
1289+
"Guided Pairing") guided_pairing ;;
1290+
"Health Check") health_check ;;
1291+
"Diagnostics Bundle") diagnostics_bundle ;;
1292+
"Connection Quality") connection_quality ;;
11171293
"Settings") edit_settings ;;
11181294
"Peers (Discovery & Known)")
11191295
local peer_choice
@@ -1138,6 +1314,10 @@ require_ui
11381314

11391315
case "${1:-menu}" in
11401316
""|menu) main_menu ;;
1317+
guided-pairing|pairing|export-helper) guided_pairing ;;
1318+
health-check|doctor) health_check ;;
1319+
diagnostics-bundle|diagnostics) diagnostics_bundle ;;
1320+
connection-quality|quality) connection_quality ;;
11411321
settings) edit_settings ;;
11421322
connection|connection-behavior|reconnect) edit_connection_behavior ;;
11431323
discover) discover_and_save_peer ;;
@@ -1150,7 +1330,7 @@ case "${1:-menu}" in
11501330
tray) start_tray ;;
11511331
install-desktop-entry|install-desktop-entries) install_desktop_entry ;;
11521332
help|-h|--help)
1153-
printf 'Usage: %s [menu|settings|connection|discover|peers|tray-help|status|start|restart|stop|tray|install-desktop-entry]\n' "$(basename "${BASH_SOURCE[0]}")"
1333+
printf 'Usage: %s [menu|guided-pairing|health-check|diagnostics-bundle|connection-quality|settings|connection|discover|peers|tray-help|status|start|restart|stop|tray|install-desktop-entry]\n' "$(basename "${BASH_SOURCE[0]}")"
11541334
;;
11551335
*)
11561336
zenity --error --text="Unknown action: $1"

0 commit comments

Comments
 (0)