# I Turned My Kindle Into a Remote Desktop Client
*Because sometimes you look at an e-ink screen and think, "this could be a monitor."*
---
A few days ago I had one of those ideas that sounds absurd at first but won't leave your head: what if I could RDP into my Windows PC from my Kindle Paperwhite? Not a tablet. Not a phone. A *Kindle* — the thing designed to display static book pages at 1 frame per never.
Turns out, with enough stubbornness and a jailbroken Kindle, you absolutely can. Here's how I built `kindle-rdp`, a native RDP client that runs directly on a Kindle's e-ink display.

*Yes, that's Claude Code. On a Kindle.*
## Why?
The honest answer: because I wanted to see if it was possible. But there's a semi-practical angle too. E-ink displays are incredible in sunlight, use almost no power, and are easy on the eyes for text-heavy work. If you're SSH-ing into a server, reviewing code, or doing anything that's mostly text — an e-ink RDP session is actually kind of pleasant.
Also, I had a Kindle Paperwhite sitting in a drawer doing nothing.
## The Tech Stack
Here's what goes into making this work:
- **FreeRDP 3.x** — the open-source RDP protocol library, handles all the Windows Remote Desktop communication
- **FBInk** — a library purpose-built for writing to Kindle framebuffers, handles all the e-ink quirks
- **evdev** — Linux input subsystem, reads raw touch events from the Kindle's touchscreen
- **A custom on-screen keyboard** — because you need to type your password somehow
Everything is cross-compiled for ARM and statically linked into a single binary. No dependencies on the device. Just drop it on the Kindle and run.
## The Build Process (a.k.a. Dependency Hell)
Cross-compiling for Kindle is not for the faint of heart. The Kindle Paperwhite runs a stripped-down Linux on an ARM Cortex-A7 with a hard-float ABI. You need a specific toolchain (`kindlehf` from koxtoolchain) and every dependency has to be built from source as a static library.
The dependency chain looks like this:
```
koxtoolchain → zlib → OpenSSL 3.x → FreeRDP 3.x → FBInk → kindle-rdp
```
Each one has its own build quirks. OpenSSL's cross-compile prefix concatenated with the exported `CC` variable, giving me gems like `arm-kindlehf-linux-gnueabihf-arm-kindlehf-linux-gnueabihf-gcc` (yes, the prefix doubled). FreeRDP wanted ICU, fuse3, systemd, and a dozen other things that make no sense on a Kindle. FBInk's `MINIMAL` build mode stripped out the font rendering I actually needed.
Eventually, after fighting every `CMakeLists.txt` in the dependency tree, I got a 5.3MB static ARM binary. Not bad for a full RDP client.
## How It Works
The architecture is deceptively simple:
```
RDP Server → FreeRDP → GDI Buffer (XRGB32) → Grayscale → FBInk → E-ink
Touchscreen → evdev → Coordinate Scaling → RDP Mouse Events → Server
```
A single-threaded event loop multiplexes FreeRDP's network events with the touch input file descriptor using `WaitForMultipleObjects`. When the RDP server sends a screen update, FreeRDP's `EndPaint` callback fires. I grab the dirty region from the GDI buffer, convert the XRGB32 pixels to 8-bit grayscale using fixed-point luminance math, and blit it to the framebuffer through FBInk.
```c
// The pixel conversion is straightforward
gray = (77 * R + 150 * G + 29 * B) >> 8;
```
For touch input, I read raw evdev events, scale the coordinates from the touchscreen's axis range to the RDP session dimensions, and send them as mouse events to the server.
## The E-ink Challenge
Here's where it gets interesting. E-ink displays were designed to show static content. They refresh slowly, they ghost, and they have multiple "waveform modes" that trade speed for quality:
- **A2**: Blazing fast (~120ms), but only black and white. No grays. Leaves ghosting everywhere.
- **DU**: Supports 16 gray levels, but noticeably slower (~260ms).
- **GC16**: Full quality, accurate grays, but takes ~450ms and flashes the screen.
I tried several strategies. Pure A2 was fast but the screen became an unreadable mess of ghost images within seconds. Pure DU was clean but felt sluggish. The solution I landed on: **A2 for all partial updates, with a time-based GC16 full-screen refresh every 3 seconds**.
This gives you responsive interaction most of the time, and every few seconds the screen does a clean refresh that wipes away all the ghosting. It's not perfect — you can see artifacts accumulate between refreshes — but it's the best balance I found between speed and readability.
## The On-Screen Keyboard
You need to enter an IP address, username, and password before connecting. The Kindle's built-in keyboard only works within the stock OS, which we've stopped at this point (you have to kill the Kindle framework to get direct framebuffer access).
So I built a custom on-screen keyboard from scratch. And I mean *from scratch* — direct framebuffer mmap, a custom 5x7 bitmap font, pixel-by-pixel rendering. No UI toolkit, no text rendering library. Just raw pixel writes to `/dev/fb0`.

*A full QWERTY keyboard drawn pixel by pixel on e-ink. With shift and symbols modes.*
The keyboard supports lowercase, uppercase (shift), and a symbols layer for all the special characters you need in passwords. Each key is a touch target mapped to the evdev coordinate space. It's not pretty, but it works.
One fun bug: I initially used characters like `!` and `^` as internal identifiers for special keys (shift, backspace, OK). Which meant you couldn't type `!` in your password. Took me longer to figure out than I'd like to admit. The fix was switching to control characters (`\x01` through `\x06`) for special keys.
## Touch Input
Mapping a touchscreen to a mouse cursor introduces some interesting UX challenges. On a normal touchscreen, you expect a tap to be a click. But to the system, a tap is really: finger down → tiny involuntary finger movement → finger up. That tiny movement? Windows interprets it as a drag, and suddenly you've selected all the text on the screen instead of clicking a button.
The fix is a dead zone. I buffer the initial touch-down position and don't send any mouse events until the finger either:
- Lifts up (it was a tap → send click at the original position)
- Moves more than 15 pixels (it's a drag → send button-down and start tracking movement)
Long press (>500ms) maps to right-click. It's not perfect — you miss hover effects and scroll — but for basic desktop interaction it works surprisingly well.
## The KUAL Integration
The Kindle needs to run our binary instead of its normal reading UI. This is handled through KUAL (Kindle Unified Application Launcher), which is basically a homebrew app launcher for jailbroken Kindles.
The tricky part: KUAL runs *inside* the Kindle framework, and we need to *stop* the framework to get framebuffer access. If you just call `stop framework` from a KUAL script, you kill KUAL and your own script along with it.
The solution is `setsid` — launch the worker script in its own process session before killing the framework:
```bash
setsid sh -c "exec run.sh >crash.log 2>&1" &
```
This creates a fully detached process that survives the framework shutdown. When the RDP session ends, the script restarts the framework and you're back to your normal Kindle home screen.
## The Result

*Working RDP session. It's slow, it ghosts, and it's glorious.*
It works. You can connect to a Windows PC, see the desktop, click on things, open applications. Is it a good experience? That depends on your definition. The refresh rate is measured in seconds, not frames per second. Ghosting is a constant companion. And without a physical keyboard, any serious text input requires a Bluetooth keyboard or a lot of patience with the on-screen one.
But for what it is — a native RDP client running on a $30 e-reader with a 6" screen — it's honestly kind of magical. Checking a server dashboard, reading through logs, reviewing a PR in a web browser — these are all things you can legitimately do from a Kindle now.
## The Temperature Hack
Here's a trick I discovered while SSH-ing into the Kindle: the e-ink controller (EPDC) uses the ambient temperature to decide how aggressively to drive the display. Warmer temperatures mean the e-ink particles move faster, so the controller can use shorter waveform timings. The Kindle exposes this as a sysfs parameter:
```
/sys/devices/soc0/soc/2000000.aips-bus/20f4000.epdc/temperature_override
```
By default, it reads the actual screen temperature — usually around 22°C indoors. But you can override it:
```bash
echo 45 > /sys/devices/soc0/soc/2000000.aips-bus/20f4000.epdc/temperature_override
```
Telling the EPDC "it's 45°C" makes it use faster waveform timings because it thinks the particles are already moving quickly. The result: noticeably snappier screen updates. It's essentially overclocking the display by lying about the temperature.
Is this safe? The display still uses the same waveform modes — it just uses the "warm weather" variant of each mode, which drives the pixels harder. In theory this could reduce display longevity over time, but for a project like this where you're using the Kindle as a secondary monitor for a few hours, it's a worthwhile tradeoff. The setting resets on reboot, so there's no permanent change.
I set the override to 45°C in the launch script and the improvement in responsiveness is immediately noticeable. It won't turn your e-ink into an LCD, but it shaves off enough milliseconds to make the interaction feel less sluggish.
## What I Learned
1. **Cross-compilation is still painful in 2026.** Every library has its own build system quirks, and they compound when you're targeting an unusual platform.
2. **E-ink is fascinating but unforgiving.** The waveform mode system is a genuinely interesting engineering tradeoff. There's no "good" mode — just different compromises between speed, accuracy, and ghosting.
3. **Touch-to-mouse mapping is harder than it sounds.** The dead zone concept seems obvious in retrospect, but the first version just sent every touch event as a mouse event and the result was unusable.
4. **Static linking is your friend on embedded devices.** One binary, no dependencies, just copy and run. Worth the build complexity.
## Try It Yourself
The code is open source. You'll need a jailbroken Kindle and some patience with the build process, but everything you need is in the repo:
**[github.com/CNuhlar/kindle-rdp](https://github.com/CNuhlar/kindle-rdp)**
Fair warning: this is very much a "works on my device" situation. It's been tested on a Kindle Paperwhite with firmware 5.16.3. Your mileage may vary. But if you've got a Kindle collecting dust and a taste for the absurd, give it a shot.
---
*If you enjoyed this, I occasionally write about other projects that probably shouldn't exist. Follow me for more questionable engineering decisions.*