Taking Screenshots on the Kobo

It would be nice to have the ability to take a screenshot from your Kobo E-Reader, for example, to share a rendered passage with friends. As it turns out there is an option to enable taking screenshots by long-pressing the power button, but, unfortunately, it replaces the main function of the power button too. However, it's pretty easy to take screenshots from the shell.

Taking Screenshots from the Shell

To take a screenshot from a Telnet or SSH shell you, read from the Framebuffer /dev/fb0 and convert it from raw RGB32 to a regular image format, such as PNG.

This small Go program does that conversion. In the RGB32 format, every color is represented by one byte along with an additional byte for the transparency component (hence 32 bit for one pixel). Here it's actually BGRA anot not RGBA. All there is to do is: read the Framebuffer, calculate the color for the current pixel and write it in the corresponding position to a PNG file.

package main

import (
	"bufio"
	"flag"
	"image"
	"image/color"
	"image/png"
	"io"
	"log"
	"os"
)

func main() {
	width := flag.Int("width", 1088, "Screen width")
	height := flag.Int("height", 1440, "Screen height")
	fb := flag.String("fb", "/dev/fb0", "Framebuffer path")
	out := flag.String("out", "screenshot.png", "Outfile")
	flag.Parse()
	f, err := os.Open(*fb)
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()
	reader := bufio.NewReader(f)
	img := image.NewRGBA(image.Rect(0, 0, *width, *height))
	for i := 0; i < (*width)*(*height); i++ {
		buf := make([]byte, 4)
		_, err := io.ReadFull(reader, buf)
		if err != nil {
			if err.Error() == "EOF" {
				break
			}
			log.Fatalln("ReadFull:", err)
		}
		x := i % *width
		y := int(i / *width)
		img.Set(x, y, color.RGBA{
			B: buf[0],
			G: buf[1],
			R: buf[2],
			A: buf[3],
		})
	}
	outf, err := os.Create(*out)
	if err != nil {
		log.Fatal(err)
	}
	defer outf.Close()
	if err := png.Encode(outf, img); err != nil {
		log.Fatal(err)
	}
}

Compile it and upload to the Kobo:

GOOS=linux GOARCH=arm GOARM=5 go build -o rgb32ToPNG
scp rgb32ToPNG root@kobo.lan:/bin

Then run like this:

rgb32ToPNG -out new-screenshot.png

The width and height can be found like this:

find /sys/devices -type d -name graphics
# -> /sys/devices/platform/soc/2000000.aips-bus/20f4000.epdc/graphics
cat /sys/devices/platform/soc/2000000.aips-bus/20f4000.epdc/graphics/fb0/virtual_size
# -> 1088,1536

With the given virtual_size, however, there is a little bar on the right side which I removed by adjusting it a bit.

Still, this isn't quite the desired behavior since it requires us to be logged into a shell from another computer. Instead, it would be better to be able to perform some kind of gesture on the screen and trigger a screenshot through that. I haven't yet looked into how the firmware handles touch input, or whether the power button could maybe be used without replacing its original behavior. If you know more please contact me by clicking Contact below.

The result is an image such as this (taken from my previous article Offline Wikipedia on the Kobo):

Finally, it would also be nice to have those screenshots synced to a phone or computer, for example using syncthing or rsync. The only problem would be that the Kobo turns off its WiFi after a while and so that the image wouldn't be synced. Also, it would be nice to have a way to enable the WiFi manually from a script. Yet another thing to research...