xconnect: A Deep Dive into Building a KDE Connect Alternative from Scratch
Why We Built xconnect
Let’s cut to the chase: KDE Connect is a fantastic project, but it comes with a dependency chain that makes it unsuitable for lightweight, GTK-based desktop environments. If you’re running Cinnamon, MATE, or XFCE on Arch, you shouldn’t need to pull in half of KDE’s infrastructure just to get clipboard sync and notification mirroring working.
So we built xconnect — a fork of mconnect, Maciej Borzecki’s excellent KDE Connect protocol implementation in Vala/C. We kept the solid protocol foundation and added a GTK3/XApp GUI, expanded the CLI, and implemented features that the original project never prioritized. No KDE dependencies. Just pure functionality.
Technical Architecture
Built on top of mconnect’s Vala/C foundation, xconnect extends the architecture with three components:
-
xconnect daemon — The core service handling protocol negotiation, device discovery, TLS channel management, and packet routing via D-Bus (
org.xconnect). -
xconnectctl — A CLI client that exposes every daemon capability as a command-line interface. This isn’t just a toy wrapper; it’s a fully-featured automation tool.
-
xconnect-app — A GTK3 system tray application with device management, notification history, MPRIS media controls, and inline configuration editing.
The daemon runs as a systemd user unit, listening on UDP 1716 for incoming device discovery and broadcasting identity packets to UDP 1714 every 5 seconds. Device connections are established over TCP 1714, with TLS negotiation handled via GnuTLS.
Features Implemented
Here’s what’s working as of v2.3.0:
| Category | Capabilities |
|---|---|
| Device Management | Discovery, pairing (with SHA-256 verification keys), unpairing, persistent short IDs |
| Notifications | Full mirroring, action buttons, inline reply (Cinnamon native), dismissal |
| File Sharing | File, URL, and text snippet transfer via kdeconnect.share.request |
| Clipboard | Bidirectional sync via xclip watcher |
| Remote Input | Touchpad/mouse control (libxtst), presentation pointer |
| Media Control | MPRIS integration, system volume control |
| Telephony | SMS sending, call mute, find my phone |
| System | Battery telemetry, cellular connectivity reports, screensaver inhibit, device lock |
The Nasty Bits: Roadblocks and Fixes
Dual-Bus D-Bus Nightmare
The most insidious bug we encountered was the dual-bus problem. Here’s what happened:
- The xconnect daemon registers on dbus-broker (
/run/user/1000/bus) — the modern, high-performance D-Bus implementation used by systemd. - Terminals and the GUI were connecting to the legacy dbus-daemon.
- Result:
xconnectctl list-deviceswould show nothing, while the GUI worked fine.
The fix required probing the systemd user bus first, then falling back to the session bus:
// In xconnectctl — try systemd user bus first
string bus_address = Environment.get_variable("DBUS_SESSION_BUS_ADDRESS");
if (bus_address == null || !bus_address.contains("/run/user/")) {
bus_address = "unix:path=/run/user/" + UserId.to_string() + "/bus";
}
We also removed the D-Bus activation file (extra/org.xconnect.service) from installation — it was causing the legacy dbus-daemon to auto-spawn a second daemon instance with no TCP listener. Classic.
Packet Queuing During TLS Handshake
Devices would occasionally fail to receive packets during the initial connection phase. Root cause: the daemon was trying to send packets before the TLS handshake completed.
The solution was a packet queue in Device.send():
public void send(Json.Object pkt) {
if (this._channel == null || !this._is_active) {
this._pending_packets.add(pkt); // Queue silently
return;
}
// ... send immediately
}
Packets queue silently when the channel isn’t ready, then flush automatically after secure_incoming_channel() completes. No more lost packets.
Messenger Reaction Emoji Fallback
Here’s a fun one. Android’s background activity launch restrictions prevent PendingIntent.getActivity() from working when the phone screen is off. This means Messenger’s “Like” reaction button fails silently on the phone.
Our workaround: a Smart Emoji Sniffer that intercepts blocked action buttons and routes reaction emojis (❤️, 🔥, 👍, 💞) via RemoteInput reply instead:
// notification.vala — intercept blocked actions
if (pkg == "com.facebook.orca" && key == "Like") {
// Route via RemoteInput instead of PendingIntent
send_emoji_fallback(device, id, emoji);
}
It’s a hack, but it works. And it’s configurable per-package.
Cinnamon Inline Reply Integration
Cinnamon’s notification applet supports inline reply via org.freedesktop.Notifications.NotificationReplied D-Bus signal. We integrated this natively:
- Daemon emits
notification_receivedsignal withhas_reply: trueandreply_labelmetadata. - GUI shows an inline reply entry at the bottom of the device detail page.
- User types reply →
SendReply()D-Bus call → daemon sendskdeconnect.notification.replypacket withrequestReplyId.
No zenity popups. No separate dialogs. Just native desktop integration.
The CLI: Power in Simplicity
Here’s where xconnect really shines for automation. Every D-Bus method is exposed as a CLI command with smart device targeting:
# Target by short ID
xconnectctl share-file 80eeceab /path/to/photo.jpg
# Target by name (partial match)
xconnectctl send-sms Galaxy "+1234567890" "Meeting at 5 PM"
# Target by index
xconnectctl notifications 0
# Target by full D-Bus path
xconnectctl ping /org/xconnect/device/80eeceab "Hello from terminal"
This means you can script workflows:
#!/bin/bash
# Daily morning routine
xconnectctl show-battery Galaxy
xconnectctl show-connectivity Galaxy
xconnectctl share-url Galaxy "https://calendar.example.com/today"
Or integrate with other tools:
# Share clipboard content with phone
xconnectctl share-text 0 "$(xclip -selection clipboard -o)"
# Forward notification to Telegram bot
xconnectctl notifications 0 | grep -i "urgent" | curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" -d chat_id=$CHAT_ID -d text="$(cat)"
The CLI isn’t an afterthought — it’s a first-class citizen designed for power users and automation engineers.
What’s Next
The roadmap (see FEATURES.md) includes:
- Priority 1: SFTP remote filesystem access — mount phone storage via
sshfs/gio mount - Priority 2: SMS conversation threads, MMS support, configurable app reaction fallback engine
- Priority 3: Contacts sync, digitizer/stylus support, share input devices
Try It Out
# Arch Linux
git clone https://github.com/Twilight0/xconnect
cd xconnect
makepkg -si
# Enable and start
systemctl --user enable --now xconnect
# List devices
xconnectctl list-devices
No KDE dependencies. Just Vala, GTK3, and a clean D-Bus interface.
xconnect is a fork of mconnect by Maciej Borzecki. We’re grateful for the solid protocol foundation that made this project possible.
Got feedback? Found a bug? Open an issue on GitHub or join the discussion on the AliveOS Forum.
