Files
obsidian-vault/wiki/tech/htpc-magic4pc.md
T

196 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: HTPC — magic4pc (LG Magic Remote)
created: '2026-05-28'
updated: '2026-05-29'
type: tech
namespace: family
tags: [htpc, how-to, pitfalls]
sources:
- family/how-to/htpc-magic4pc.md
- family/how-to/magic4pc-webos.md
confidence: medium
related:
- "[[htpc-bazzite-proton]]"
- "[[tech/htpc-steam-emulators]]"
- "[[tech/tv-luna-send]]"
---
# HTPC — magic4pc (LG Magic Remote)
Go-client for using the LG Magic Remote (webOS TV app) as HTPC
mouse/keyboard input. Fork: `mallexxx/magic4pc_altclient` branch
`bazzite-linux`.
LG TV: `192.168.1.75`, HTPC: `192.168.1.86`.
## Architecture
- **TV server**: webOS app on LG TV → sends UDP to fixed source
port **9106**
- **HTPC client**: `/usr/local/bin/magic4pc`, UDP listener
- **xdotool**: single persistent process via stdin pipe (mouse +
keyboard)
- **ydotool**: gamescope-specific keys (Ctrl+1, Ctrl+2) via
`/run/user/1000/.ydotool_socket`
Session detection:
- `isGamescopeSession()` — scans `/proc/*/cmdline` for `kwin_wayland`;
if absent → gamescope (Steam Big Picture mode)
- `activeDisplay()` — KDE uses Xwayland display from kwin args; gamescope
uses `:1` (game) if visible windows present, else `:0` (Steam BP)
## Button Mapping
| Button | Keycode | Gamescope | KDE Desktop |
|--------|---------|-----------|-------------|
| Red | 403 | Ctrl+1 (Steam menu) | Super |
| Green | 404 | Escape | Escape |
| Yellow | 405 | Ctrl+2 (Steam QAM) | Middle click |
| Blue | 406 | Right click | Right click |
| Back | 461 | Mouse X1 | Mouse X1 |
| D-pad | 37-40 | Arrow keys | Arrow keys |
| Ch+/Ch- | 33/34 | PageUp/Down | PageUp/Down |
| Play/Stop/Pause | 415/413/19 | XF86Audio* | XF86Audio* |
| Touch (swipe) | — | mousemove (scaled) | mousemove |
| Tap | — | Left click | Left click |
## Mouse Coordinate Mapping
TV sends coordinates in 01920 × 01080 space.
**In gamescope:** uses letterbox algorithm.
1. Find active window: (a) under cursor via `getmouselocation`, or
(b) largest bounding-box window (non-popup, highest ID)
2. Compute 16:9 letterbox viewport from window width:
`lb_h = win_w * 9/16`
3. Compute offset: `off_y = -(lb_h - win_h) / 2`
4. Map: `x11_x = tv_x * scale + off_x`; clip negatives to 0
**In KDE:** `getdisplaygeometry` → scale = display / 1920×1080.
## Key Pitfalls
| Problem | Fix |
|---------|-----|
| TV "service error" / won't reconnect | Fixed source UDP port 9106 |
| TV stuck "waiting for client" | Server keepalive timeout 10s |
| KDE XTest prompt on every click | Single persistent xdotool process |
| KDE XTest prompt after xdotool restart | `XwaylandEisNoPrompt=true` in kwinrc |
| Wrong mouse coordinates | Dynamic scale via `xdotool getdisplaygeometry` |
| Binary lost after `rpm-ostree upgrade` | Rebuild and redeploy |
## Deploy
```bash
# Build on Mac
cd ~/Developer/magic4pc_altclient
GOOS=linux GOARCH=amd64 go build -o magic4pc .
# Copy + restart on HTPC
scp magic4pc htpc:/home/bazzite/magic4pc/magic4pc
echo bazzite | sudo -S systemctl stop magic4pc.service
echo bazzite | sudo -S cp /home/bazzite/magic4pc/magic4pc \
/usr/local/bin/magic4pc
echo bazzite | sudo -S systemctl start magic4pc.service
```
Sudoers rule (no-password systemctl):
```
bazzite ALL=(ALL) NOPASSWD: /usr/bin/systemctl
```
## WebOS TV App — Build & Deploy
The TV-side is a React/Enact WebOS app + Node.js service bundled into
an IPK. Sources: `~/Developer/magic4pc/webos/`.
### Build
```bash
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
```
**Pitfall:** `--openssl-legacy-provider` is required — the old webpack
is incompatible with Node.js 25+.
### Package IPK
```bash
/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir .
```
**Pitfall:** both `dist/` and `service/` arguments are required —
omitting `service/` excludes the Node.js service from the IPK.
**Pitfall:** `npm run package` = `ares-package -n` (unsigned) — TV
rejects unsigned packages. Use the SDK command directly.
### Deploy (deploy.sh)
```bash
cd ~/Developer/magic4pc/webos && ./deploy.sh
```
Script flow: build → package → scp → close → remove → install → launch.
Uses [[tech/tv-luna-send]] wrapper for close/remove/launch.
For install (subscribe mode) — direct `ssh+script` with polling on
`"state":"installed"`.
**Pitfall:** `ares-install` / `ares-launch` are unreliable — they don't
wait for completion. Use `luna-send dev/install` via `script` wrapper.
### Version in UI
Displayed as `1.1.0 (YYYY-MM-DD HH:MM)` — injected by webpack at
build time via `process.env.BUILD_DATE`.
## Auto-launch on Boot/Wake
Magic4pc launches a configured app when the TV boots or wakes.
| File | Location | Purpose |
|------|----------|---------|
| `magic4pc-settings` | PERSISTENT_DIR | Selected app ID |
| `magic4pc-last-app` | PERSISTENT_DIR | Last foreground app |
| `magic4pc-run-state` | `/tmp/` | `running` after first launch |
`PERSISTENT_DIR = /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service`
Logic:
- **Boot/wake:** `/tmp` is cleared → no `run-state``freshStart=true`
→ launches configured app
- **Manual launch:** `run-state=running` already present → skips auto-launch
The `init.d` script on TV (`/var/lib/webosbrew/init.d/magic4pc`) removes
`run-state` on suspend so wake triggers fresh launch. Always edit in
repo (`tv-scripts/init.d-magic4pc.sh`), never directly on TV.
## WebOS Back Key + System Keyboard
When the system keyboard is open, **Back #1** is consumed by the OS
(closes keyboard) — the `keydown` DOM event and `onButtonDown` are
**not** fired. **Back #2** arrives normally.
Fix used in `MainPanel.js` (`_kbWasOpen` flag):
```js
// Input.onActivate:
this._kbWasOpen = true;
// onButtonDown on Back:
if (this.state.wolMacActive || this._kbWasOpen) {
this._kbWasOpen = false;
return; // suppress panel close
}
```
Approaches that don't work: `Popup.noAutoDismiss`, timeout heuristic,
polling `document.activeElement` (keyboard is a system overlay, focus
never moves to INPUT).
## See Also
- [[htpc-bazzite-proton]] — Proton game compatibility on this HTPC
- [[tech/htpc-steam-emulators]] — Steam, emulators, gamepad config
- [[tech/tv-luna-send]] — luna-send SSH wrapper for WebOS TV commands