├── .cargo
└── config.toml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE-AGPLv3
├── README.md
├── artworks
├── plato-logo.svg
├── plato.png
├── screenshot01.png
├── screenshot02.png
├── screenshot03.png
├── screenshot04.png
├── swipe_sequences.svg
├── thumbnail01.png
├── thumbnail02.png
├── thumbnail03.png
├── thumbnail04.png
└── touch_regions.svg
├── build.sh
├── bundle.sh
├── contrib
├── NickelMenu
│ └── plato
├── Settings-sample.toml
├── config-sample.sh
├── convert-dictionary.sh
├── grayscale-256.svg
├── hilbert-256.svg
├── mupdf-upgrade
│ └── expand_cmap_codes.py
├── nickel.sh
├── plato.sh
├── unicode_test.epub
└── wifi-hooks
│ ├── wifi-post-up.sh
│ └── wifi-pre-down.sh
├── crates
├── core
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ ├── battery
│ │ ├── fake.rs
│ │ ├── kobo.rs
│ │ └── mod.rs
│ │ ├── color.rs
│ │ ├── context.rs
│ │ ├── device.rs
│ │ ├── dictionary
│ │ ├── dictreader.rs
│ │ ├── errors.rs
│ │ ├── indexing.rs
│ │ ├── mod.rs
│ │ └── testdata
│ │ │ ├── case_insensitive_dict.dict
│ │ │ ├── case_insensitive_dict.index
│ │ │ ├── case_sensitive_dict.dict
│ │ │ └── case_sensitive_dict.index
│ │ ├── document
│ │ ├── djvu.rs
│ │ ├── djvulibre_sys.rs
│ │ ├── epub
│ │ │ └── mod.rs
│ │ ├── html
│ │ │ ├── css.rs
│ │ │ ├── dom.rs
│ │ │ ├── engine.rs
│ │ │ ├── layout.rs
│ │ │ ├── mod.rs
│ │ │ ├── parse.rs
│ │ │ ├── style.rs
│ │ │ └── xml.rs
│ │ ├── mod.rs
│ │ ├── mupdf_sys.rs
│ │ └── pdf.rs
│ │ ├── font
│ │ ├── freetype_sys.rs
│ │ ├── harfbuzz_sys.rs
│ │ └── mod.rs
│ │ ├── framebuffer
│ │ ├── image.rs
│ │ ├── ion_sys.rs
│ │ ├── kobo1.rs
│ │ ├── kobo2.rs
│ │ ├── linuxfb_sys.rs
│ │ ├── mod.rs
│ │ ├── mxcfb_sys.rs
│ │ ├── sunxi_sys.rs
│ │ └── transform.rs
│ │ ├── frontlight
│ │ ├── mod.rs
│ │ ├── natural.rs
│ │ ├── premixed.rs
│ │ └── standard.rs
│ │ ├── geom.rs
│ │ ├── gesture.rs
│ │ ├── helpers.rs
│ │ ├── input.rs
│ │ ├── lib.rs
│ │ ├── library.rs
│ │ ├── lightsensor
│ │ ├── kobo.rs
│ │ └── mod.rs
│ │ ├── metadata.rs
│ │ ├── rtc.rs
│ │ ├── settings
│ │ ├── mod.rs
│ │ └── preset.rs
│ │ ├── unit.rs
│ │ └── view
│ │ ├── battery.rs
│ │ ├── button.rs
│ │ ├── calculator
│ │ ├── bottom_bar.rs
│ │ ├── code_area.rs
│ │ ├── input_bar.rs
│ │ └── mod.rs
│ │ ├── clock.rs
│ │ ├── common.rs
│ │ ├── dialog.rs
│ │ ├── dictionary
│ │ ├── bottom_bar.rs
│ │ └── mod.rs
│ │ ├── filler.rs
│ │ ├── frontlight.rs
│ │ ├── home
│ │ ├── address_bar.rs
│ │ ├── book.rs
│ │ ├── bottom_bar.rs
│ │ ├── directories_bar.rs
│ │ ├── directory.rs
│ │ ├── library_label.rs
│ │ ├── mod.rs
│ │ ├── navigation_bar.rs
│ │ └── shelf.rs
│ │ ├── icon.rs
│ │ ├── image.rs
│ │ ├── input_field.rs
│ │ ├── intermission.rs
│ │ ├── key.rs
│ │ ├── keyboard.rs
│ │ ├── label.rs
│ │ ├── labeled_icon.rs
│ │ ├── menu.rs
│ │ ├── menu_entry.rs
│ │ ├── mod.rs
│ │ ├── named_input.rs
│ │ ├── notification.rs
│ │ ├── page_label.rs
│ │ ├── preset.rs
│ │ ├── presets_list.rs
│ │ ├── reader
│ │ ├── bottom_bar.rs
│ │ ├── chapter_label.rs
│ │ ├── margin_cropper.rs
│ │ ├── mod.rs
│ │ ├── results_bar.rs
│ │ ├── results_label.rs
│ │ └── tool_bar.rs
│ │ ├── rotation_values
│ │ └── mod.rs
│ │ ├── rounded_button.rs
│ │ ├── search_bar.rs
│ │ ├── sketch
│ │ └── mod.rs
│ │ ├── slider.rs
│ │ ├── top_bar.rs
│ │ └── touch_events
│ │ └── mod.rs
├── emulator
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── fetcher
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── importer
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
└── plato
│ ├── Cargo.toml
│ └── src
│ ├── app.rs
│ └── main.rs
├── css
├── annotations.css
├── bookmarks.css
├── dictionary.css
├── epub.css
├── html.css
├── sysinfo.css
└── toc.css
├── dist.sh
├── doc
├── ARTICLE_FETCHER.md
├── BUILD.md
├── GUIDE.md
├── HOOKS.md
├── LIBRARY.md
├── MANUAL.md
├── NAVIGATION.md
└── TODO.md
├── download.sh
├── fonts
├── Cormorant-Regular.ttf
├── Delius-Regular.ttf
├── LibertinusSerif-Bold.otf
├── LibertinusSerif-BoldItalic.otf
├── LibertinusSerif-Italic.otf
├── LibertinusSerif-Regular.otf
├── NotoSans-Bold.ttf
├── NotoSans-BoldItalic.ttf
├── NotoSans-Italic.ttf
├── NotoSans-Regular.ttf
├── NotoSerif-Bold.ttf
├── NotoSerif-BoldItalic.ttf
├── NotoSerif-Italic.ttf
├── NotoSerif-Regular.ttf
├── Parisienne-Regular.ttf
├── SourceCodeVariable-Italic.otf
├── SourceCodeVariable-Roman.otf
└── VarelaRound-Regular.ttf
├── icons
├── align-center.svg
├── align-justify.svg
├── align-left.svg
├── align-right.svg
├── alternate.svg
├── angle-down.svg
├── angle-left-small.svg
├── angle-left.svg
├── angle-right-small.svg
├── angle-right.svg
├── angle-up.svg
├── arrow-down.svg
├── arrow-left.svg
├── arrow-right.svg
├── arrow-up.svg
├── back.svg
├── bullet.svg
├── check_mark-large.svg
├── check_mark-small.svg
├── check_mark.svg
├── close.svg
├── combine.svg
├── contrast.svg
├── cover.svg
├── crop.svg
├── delete-backward.svg
├── delete-forward.svg
├── dodecahedron.svg
├── double_angle-left.svg
├── double_angle-right.svg
├── enclosed_menu.svg
├── font_family.svg
├── font_size.svg
├── frontlight-disabled.svg
├── frontlight.svg
├── gray.svg
├── home.svg
├── line_height.svg
├── margin.svg
├── menu.svg
├── minus.svg
├── move-backward-short.svg
├── move-backward.svg
├── move-forward-short.svg
├── move-forward.svg
├── plug.svg
├── plus.svg
├── redo.svg
├── return.svg
├── search.svg
├── shift.svg
├── toc.svg
└── undo.svg
├── install-importer.sh
├── keyboard-layouts
├── english.json
└── russian.json
├── mupdf_wrapper
├── build-kobo.sh
├── build.sh
└── mupdf_wrapper.c
├── run-emulator.sh
├── scripts
├── essid.sh
├── ip.sh
├── resume.sh
├── suspend.sh
├── usb-disable.sh
├── usb-enable.sh
├── wifi-disable.sh
└── wifi-enable.sh
├── service.sh
└── thirdparty
├── build.sh
├── bzip2
├── Makefile-kobo
└── build-kobo.sh
├── djvulibre
├── build-kobo.sh
└── kobo.patch
├── download.sh
├── freetype2
└── build-kobo.sh
├── gumbo
└── build-kobo.sh
├── harfbuzz
└── build-kobo.sh
├── jbig2dec
└── build-kobo.sh
├── libjpeg
└── build-kobo.sh
├── libpng
└── build-kobo.sh
├── mupdf
├── build-kobo.sh
└── kobo.patch
├── openjpeg
└── build-kobo.sh
└── zlib
└── build-kobo.sh
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [target.arm-unknown-linux-gnueabihf]
2 | linker = "arm-linux-gnueabihf-gcc"
3 | rustflags = ["-C", "target-feature=+v7,+vfp3,+neon"]
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.rs.bk
2 | /target
3 | /extra
4 | /archives
5 | /resources
6 | /libs
7 | /bin
8 | /Settings.toml
9 | /.metadata.json
10 | /.reading-states
11 | /.fat32-epoch
12 | /.thumbnail-previews
13 | /.trash
14 | /css/*-user.css
15 | /hyphenation-patterns
16 | /dictionaries
17 | /dist
18 | /bundle
19 | /plato-*.zip
20 | /thirdparty/*/*
21 | !/thirdparty/*/*kobo*
22 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | resolver = "2"
3 | members = [
4 | "crates/core",
5 | "crates/plato",
6 | "crates/emulator",
7 | "crates/importer",
8 | "crates/fetcher",
9 | ]
10 |
11 | [profile.release-minsized]
12 | inherits = "release"
13 | panic = "abort"
14 | codegen-units = 1
15 | opt-level = "z"
16 | lto = true
17 | strip = true
18 |
--------------------------------------------------------------------------------
/LICENSE-AGPLv3:
--------------------------------------------------------------------------------
1 | Plato -- Document reader for the Kobo e-ink devices.
2 | Copyright (C) 2017 Bastien Dejean.
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Affero General Public License as
6 | published by the Free Software Foundation, either version 3 of the
7 | License, or (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU Affero General Public License for more details.
13 |
14 | You should have received a copy of the GNU Affero General Public License
15 | along with this program. If not, see .
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | *Plato* is a document reader for *Kobo*'s e-readers.
4 |
5 | Documentation: [GUIDE](doc/GUIDE.md), [MANUAL](doc/MANUAL.md) and [BUILD](doc/BUILD.md).
6 |
7 | ## Supported firmwares
8 |
9 | Any 4.*X*.*Y* firmware, with *X* ≥ 6, will do.
10 |
11 | ## Supported devices
12 |
13 | - *Libra Colour*.
14 | - *Clara Colour*.
15 | - *Clara BW*.
16 | - *Elipsa 2E*.
17 | - *Clara 2E*.
18 | - *Libra 2*.
19 | - *Sage*.
20 | - *Elipsa*.
21 | - *Nia*.
22 | - *Libra H₂O*.
23 | - *Forma*.
24 | - *Clara HD*.
25 | - *Aura H₂O Edition 2*.
26 | - *Aura Edition 2*.
27 | - *Aura ONE*.
28 | - *Glo HD*.
29 | - *Aura H₂O*.
30 | - *Aura*.
31 | - *Glo*.
32 | - *Touch C*.
33 | - *Touch B*.
34 |
35 | ## Supported formats
36 |
37 | - PDF, CBZ, FB2, MOBI, XPS and TXT via [MuPDF](https://mupdf.com/index.html).
38 | - ePUB through a built-in renderer.
39 | - DJVU via [DjVuLibre](http://djvu.sourceforge.net/index.html).
40 |
41 | ## Features
42 |
43 | - Crop the margins.
44 | - Continuous fit-to-width zoom mode with line preserving cuts.
45 | - Rotate the screen (portrait ↔ landscape).
46 | - Adjust the contrast.
47 | - Define words using *dictd* dictionaries.
48 | - Annotations, highlights and bookmarks.
49 | - Retrieve articles from online sources through [hooks](doc/HOOKS.md) (an example *wallabag* [article fetcher](doc/ARTICLE_FETCHER.md) is provided).
50 |
51 | [](artworks/screenshot01.png) [](artworks/screenshot02.png) [](artworks/screenshot03.png) [](artworks/screenshot04.png)
52 |
53 | ## Donations
54 |
55 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KNAR2VKYRYUV6)
56 |
--------------------------------------------------------------------------------
/artworks/plato-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/artworks/plato.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/plato.png
--------------------------------------------------------------------------------
/artworks/screenshot01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/screenshot01.png
--------------------------------------------------------------------------------
/artworks/screenshot02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/screenshot02.png
--------------------------------------------------------------------------------
/artworks/screenshot03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/screenshot03.png
--------------------------------------------------------------------------------
/artworks/screenshot04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/screenshot04.png
--------------------------------------------------------------------------------
/artworks/swipe_sequences.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/artworks/thumbnail01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/thumbnail01.png
--------------------------------------------------------------------------------
/artworks/thumbnail02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/thumbnail02.png
--------------------------------------------------------------------------------
/artworks/thumbnail03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/thumbnail03.png
--------------------------------------------------------------------------------
/artworks/thumbnail04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/artworks/thumbnail04.png
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | set -e
4 |
5 | method=${1:-"fast"}
6 |
7 | [ -e libs -a $# -eq 0 ] && method=skip
8 |
9 | case "$method" in
10 | fast)
11 | ./download.sh 'libs/*'
12 | cd libs
13 |
14 | ln -s libz.so.1 libz.so
15 | ln -s libbz2.so.1.0 libbz2.so
16 |
17 | ln -s libpng16.so.16 libpng16.so
18 | ln -s libjpeg.so.9 libjpeg.so
19 | ln -s libopenjp2.so.7 libopenjp2.so
20 | ln -s libjbig2dec.so.0 libjbig2dec.so
21 |
22 | ln -s libfreetype.so.6 libfreetype.so
23 | ln -s libharfbuzz.so.0 libharfbuzz.so
24 |
25 | ln -s libgumbo.so.1 libgumbo.so
26 | ln -s libdjvulibre.so.21 libdjvulibre.so
27 |
28 | cd ../thirdparty
29 | ./download.sh mupdf
30 | cd ..
31 | ;;
32 |
33 | slow)
34 | shift
35 | cd thirdparty
36 | ./download.sh "$@"
37 | ./build.sh "$@"
38 | cd ..
39 |
40 | [ -e libs ] || mkdir libs
41 |
42 | cp thirdparty/zlib/libz.so libs
43 | cp thirdparty/bzip2/libbz2.so libs
44 |
45 | cp thirdparty/libpng/.libs/libpng16.so libs
46 | cp thirdparty/libjpeg/.libs/libjpeg.so libs
47 | cp thirdparty/openjpeg/build/bin/libopenjp2.so libs
48 | cp thirdparty/jbig2dec/.libs/libjbig2dec.so libs
49 |
50 | cp thirdparty/freetype2/objs/.libs/libfreetype.so libs
51 | cp thirdparty/harfbuzz/src/.libs/libharfbuzz.so libs
52 |
53 | cp thirdparty/gumbo/.libs/libgumbo.so libs
54 | cp thirdparty/djvulibre/libdjvu/.libs/libdjvulibre.so libs
55 | cp thirdparty/mupdf/build/release/libmupdf.so libs
56 | ;;
57 |
58 | skip)
59 | ;;
60 | *)
61 | printf "Unknown build method: %s.\n" "$method" 1>&2
62 | exit 1
63 | ;;
64 | esac
65 |
66 | cd mupdf_wrapper
67 | ./build-kobo.sh
68 | cd ..
69 |
70 | cargo build --release --target=arm-unknown-linux-gnueabihf -p plato
71 |
--------------------------------------------------------------------------------
/bundle.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | if [ "$#" -lt 1 ] ; then
4 | printf "Usage: %s NICKEL_MENU_ARCHIVE\n" "${0##*/}" >&2
5 | exit 1
6 | fi
7 |
8 | [ -d dist ] || ./dist.sh
9 | [ -d bundle ] && rm -Rf bundle
10 |
11 | NICKEL_MENU_ARCHIVE=$1
12 |
13 | mkdir bundle
14 | cd bundle || exit 1
15 |
16 | if gzip -tq "$NICKEL_MENU_ARCHIVE"; then
17 | ln -s "$NICKEL_MENU_ARCHIVE" KoboRoot.tgz
18 | else
19 | unzip "$NICKEL_MENU_ARCHIVE" KoboRoot.tgz
20 | fi
21 |
22 | tar -xzvf KoboRoot.tgz
23 | rm KoboRoot.tgz
24 | mv mnt/onboard/.adds .
25 | rm -Rf mnt
26 |
27 | mv ../dist .adds/plato
28 | cp ../contrib/NickelMenu/* .adds/nm
29 |
30 | mkdir .kobo
31 | tar -czvf .kobo/KoboRoot.tgz usr
32 | rm -Rf usr
33 |
34 | FIRMWARE_VERSION=$(basename "$FIRMWARE_ARCHIVE" .zip)
35 | FIRMWARE_VERSION=${FIRMWARE_VERSION##*-}
36 | PLATO_VERSION=$(cargo pkgid -p plato | cut -d '#' -f 2)
37 |
38 | zip -r plato-bundle-"$PLATO_VERSION".zip .adds .kobo
39 | rm -Rf .adds .kobo
40 |
--------------------------------------------------------------------------------
/contrib/NickelMenu/plato:
--------------------------------------------------------------------------------
1 | menu_item :main :Plato :cmd_spawn :quiet:/mnt/onboard/.adds/plato/plato.sh
2 |
--------------------------------------------------------------------------------
/contrib/config-sample.sh:
--------------------------------------------------------------------------------
1 | # Don't set the framebuffer's depth.
2 | # unset PLATO_SET_FRAMEBUFFER_DEPTH
3 |
4 | # Don't convert StarDict dictionaries.
5 | # unset PLATO_CONVERT_DICTIONARIES
6 |
7 | # Disable hyphenation.
8 | # [ -d hyphenation-patterns ] && rm -rf hyphenation-patterns
9 |
--------------------------------------------------------------------------------
/contrib/convert-dictionary.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | # Converts a StarDict dictionary to the dictd format.
3 | # The first argument must be the path to the IFO file.
4 |
5 | trap 'exit 1' ERR
6 |
7 | base=${1%.*}
8 | bindir=bin/utils
9 | short_name=$(grep '^bookname=' "$1" | cut -d '=' -f 2)
10 | url=$(grep '^website=' "$1" | cut -d '=' -f 2)
11 |
12 | echo "Converting ${short_name} (${1})."
13 |
14 | [ -e "${base}.dict.dz" ] && "$bindir"/dictzip -d "${base}.dict.dz"
15 |
16 | args="${base}.dict"
17 |
18 | [ -e "${base}.syn" ] && args="$args ${base}.syn"
19 |
20 | # shellcheck disable=SC2086
21 | "$bindir"/sdunpack $args < "${base}.idx" > "${base}.txt"
22 | [ "${short_name%% *}" = "Wiktionary" ] && sed -i 's/^\([\[/].*\)/
\1<\/p>/' "${base}.txt"
23 | "$bindir"/dictfmt --quiet --utf8 --index-keep-orig --headword-separator '|' -s "$short_name" -u "$url" -t "$base" < "${base}.txt"
24 | "$bindir"/dictzip "${base}.dict"
25 |
26 | rm "$1" "${base}.idx" "${base}.txt"
27 | [ -e "${base}.syn" ] && rm "${base}.syn"
28 |
--------------------------------------------------------------------------------
/contrib/mupdf-upgrade/expand_cmap_codes.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | import re
4 | import sys
5 |
6 | for line in sys.stdin:
7 | m = re.search(r'startCharCode = (\d+), endCharCode = (\d+),', line)
8 | if m is not None:
9 | [start, end] = [int(s) for s in m.groups()]
10 | for i in range(start, end+1):
11 | print(i)
12 |
--------------------------------------------------------------------------------
/contrib/nickel.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | export LD_LIBRARY_PATH=/usr/local/Kobo
4 | export QT_GSTREAMER_PLAYBIN_AUDIOSINK=alsasink
5 | export QT_GSTREAMER_PLAYBIN_AUDIOSINK_DEVICE_PARAMETER=bluealsa:DEV=00:00:00:00:00:00
6 |
7 | (
8 | if [ "$PLATFORM" = "freescale" ] || [ "$PLATFORM" = "mx50-ntx" ] || [ "$PLATFORM" = "mx6sl-ntx" ]; then
9 | usleep 400000
10 | fi
11 | /etc/init.d/on-animator.sh
12 | ) &
13 |
14 | # Let Nickel remounts the SD card read only.
15 | [ -e /dev/mmcblk1p1 ] && umount /mnt/sd
16 |
17 | # Nickel wants the WiFi to be down when it starts
18 | ./scripts/wifi-disable.sh
19 |
20 | # Reset PWD to a sane value, outside of onboard, so that USBMS behaves properly
21 | cd /
22 | # And clear up our own stuff from the env while we're there
23 | unset OLDPWD SERIAL_NUMBER FIRMWARE_VERSION MODEL_NUMBER PRODUCT_ID
24 |
25 | /usr/local/Kobo/hindenburg &
26 | LIBC_FATAL_STDERR_=1 /usr/local/Kobo/nickel -platform kobo -skipFontLoad &
27 | [ "$PLATFORM" != "freescale" ] && udevadm trigger &
28 |
--------------------------------------------------------------------------------
/contrib/plato.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | WORKDIR=$(dirname "$0")
4 | cd "$WORKDIR" || exit 1
5 |
6 | PLATO_SET_FRAMEBUFFER_DEPTH=1
7 | PLATO_CONVERT_DICTIONARIES=1
8 |
9 | # shellcheck disable=SC1091
10 | [ -e config.sh ] && . config.sh
11 |
12 | # shellcheck disable=SC2046
13 | export $(grep -sE '^(INTERFACE|WIFI_MODULE|DBUS_SESSION_BUS_ADDRESS|NICKEL_HOME|LANG)=' /proc/"$(pidof -s nickel)"/environ)
14 | sync
15 | killall -TERM nickel hindenburg sickel fickel adobehost foxitpdf iink dhcpcd-dbus dhcpcd fmon > /dev/null 2>&1
16 |
17 |
18 | if [ -e /sys/class/leds/LED ] ; then
19 | LEDS_INTERFACE=/sys/class/leds/LED/brightness
20 | STANDARD_LEDS=1
21 | elif [ -e /sys/class/leds/GLED ] ; then
22 | LEDS_INTERFACE=/sys/class/leds/GLED/brightness
23 | STANDARD_LEDS=1
24 | elif [ -e /sys/class/leds/bd71828-green-led ] ; then
25 | LEDS_INTERFACE=/sys/class/leds/bd71828-green-led/brightness
26 | STANDARD_LEDS=1
27 | elif [ -e /sys/devices/platform/ntx_led/lit ] ; then
28 | LEDS_INTERFACE=/sys/devices/platform/ntx_led/lit
29 | STANDARD_LEDS=0
30 | elif [ -e /sys/devices/platform/pmic_light.1/lit ] ; then
31 | LEDS_INTERFACE=/sys/devices/platform/pmic_light.1/lit
32 | STANDARD_LEDS=0
33 | fi
34 |
35 | # Turn off the LEDs
36 | if [ "$STANDARD_LEDS" -eq 1 ] ; then
37 | echo 0 > "$LEDS_INTERFACE"
38 | else
39 | # https://www.tablix.org/~avian/blog/archives/2013/03/blinken_kindle/
40 | for ch in 3 4 5; do
41 | echo "ch ${ch}" > "$LEDS_INTERFACE"
42 | echo "cur 1" > "$LEDS_INTERFACE"
43 | echo "dc 0" > "$LEDS_INTERFACE"
44 | done
45 | fi
46 |
47 | # Remount the SD card read-write if it's mounted read-only
48 | grep -q ' /mnt/sd .*[ ,]ro[ ,]' /proc/mounts && mount -o remount,rw /mnt/sd
49 |
50 | # Define environment variables used by `scripts/usb-*.sh`
51 | KOBO_TAG=/mnt/onboard/.kobo/version
52 | if [ -e "$KOBO_TAG" ] ; then
53 | SERIAL_NUMBER=$(cut -f 1 -d ',' "$KOBO_TAG")
54 | FIRMWARE_VERSION=$(cut -f 3 -d ',' "$KOBO_TAG")
55 | MODEL_NUMBER=$(cut -f 6 -d ',' "$KOBO_TAG" | sed -e 's/^[0-]*//')
56 |
57 | # This is a combination of the information given in `FBInk/fbink_device_id.c`
58 | # and `calibre/src/calibre/devices/kobo/driver.py`.
59 | case "$MODEL_NUMBER" in
60 | 3[12]0) PRODUCT_ID=0x4163 ;; # Touch A/B, Touch C
61 | 330) PRODUCT_ID=0x4173 ;; # Glo
62 | 340) PRODUCT_ID=0x4183 ;; # Mini
63 | 350) PRODUCT_ID=0x4193 ;; # Aura HD
64 | 360) PRODUCT_ID=0x4203 ;; # Aura
65 | 370) PRODUCT_ID=0x4213 ;; # Aura H₂O
66 | 371) PRODUCT_ID=0x4223 ;; # Glo HD
67 | 372) PRODUCT_ID=0x4224 ;; # Touch 2.0
68 | 373|381) PRODUCT_ID=0x4225 ;; # Aura ONE, Aura ONE Limited Edition
69 | 374) PRODUCT_ID=0x4227 ;; # Aura H₂O Edition 2
70 | 375) PRODUCT_ID=0x4226 ;; # Aura Edition 2
71 | 376) PRODUCT_ID=0x4228 ;; # Clara HD
72 | 377|380) PRODUCT_ID=0x4229 ;; # Forma, Forma 32GB
73 | 384) PRODUCT_ID=0x4232 ;; # Libra H₂O
74 | 382) PRODUCT_ID=0x4230 ;; # Nia
75 | 387) PRODUCT_ID=0x4233 ;; # Elipsa
76 | 383) PRODUCT_ID=0x4231 ;; # Sage
77 | 388) PRODUCT_ID=0x4234 ;; # Libra 2
78 | 386) PRODUCT_ID=0x4235 ;; # Clara 2E
79 | 389) PRODUCT_ID=0x4236 ;; # Elipsa 2E
80 | 390) PRODUCT_ID=0x4237 ;; # Libra Colour
81 | 393) PRODUCT_ID=0x4238 ;; # Clara Colour
82 | 391) PRODUCT_ID=0x4239 ;; # Clara BW
83 | *) PRODUCT_ID=0x6666 ;;
84 | esac
85 |
86 | export SERIAL_NUMBER FIRMWARE_VERSION MODEL_NUMBER PRODUCT_ID
87 | fi
88 |
89 | export LD_LIBRARY_PATH="libs:${LD_LIBRARY_PATH}"
90 |
91 | [ -e info.log ] && [ "$(stat -c '%s' info.log)" -gt $((1<<18)) ] && mv info.log archive.log
92 |
93 | [ "$PLATO_CONVERT_DICTIONARIES" ] && find -L dictionaries -name '*.ifo' -exec ./convert-dictionary.sh {} \;
94 |
95 | if [ "$PLATO_SET_FRAMEBUFFER_DEPTH" ] ; then
96 | case "${PRODUCT}:${MODEL_NUMBER}" in
97 | kraken:*|pixie:*|dragon:*|phoenix:*|dahlia:*|alyssum:*|pika:*|daylight:*|star:375|snow:374)
98 | ORIG_BPP=$(./bin/utils/fbdepth -g)
99 | ;;
100 | *)
101 | unset ORIG_BPP
102 | ;;
103 | esac
104 | fi
105 |
106 | [ "$ORIG_BPP" ] && ./bin/utils/fbdepth -q -d 8
107 |
108 | LIBC_FATAL_STDERR_=1 ./plato >> info.log 2>&1
109 |
110 | [ "$ORIG_BPP" ] && ./bin/utils/fbdepth -q -d "$ORIG_BPP"
111 |
112 | if [ -e /tmp/reboot ] ; then
113 | reboot
114 | elif [ -e /tmp/power_off ] ; then
115 | poweroff -f
116 | else
117 | ./nickel.sh &
118 | fi
119 |
--------------------------------------------------------------------------------
/contrib/unicode_test.epub:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baskerville/plato/0d8303af63d3cc5496035d29816c6dbba43d0df5/contrib/unicode_test.epub
--------------------------------------------------------------------------------
/contrib/wifi-hooks/wifi-post-up.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | # Dropbear can generate a host key automatically (-R), but the file location is
4 | # configured at build-time and many ARM builds of dropbear have weird locations.
5 | # In order to specify a location, we need to generate the key manually.
6 | if [ ! -e /etc/dropbear/dropbear_ecdsa_host_key ]; then
7 | mkdir -p /etc/dropbear
8 | /mnt/onboard/.adds/bin/dropbearkey -t ecdsa -f /etc/dropbear/dropbear_ecdsa_host_key
9 | fi
10 |
11 | # Add `-n` to skip password check, for initial user creation or password setting.
12 | /mnt/onboard/.adds/bin/dropbear -r /etc/dropbear/dropbear_ecdsa_host_key -p 2233
13 |
--------------------------------------------------------------------------------
/contrib/wifi-hooks/wifi-pre-down.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | killall dropbear
4 |
--------------------------------------------------------------------------------
/crates/core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | authors = ["Bastien Dejean "]
3 | name = "plato-core"
4 | version = "0.9.44"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["rlib"]
9 |
10 | [dependencies]
11 | bitflags = "2.6.0"
12 | downcast-rs = "1.2.1"
13 | lazy_static = "1.5.0"
14 | libc = "0.2.164"
15 | png = "0.17.14"
16 | regex = "1.11.1"
17 | serde = { version = "1.0.215", features = ["derive"] }
18 | serde_json = "1.0.133"
19 | titlecase = "3.3.0"
20 | unicode-normalization = "0.1.24"
21 | toml = "0.8.19"
22 | zip = "2.2.1"
23 | kl-hyphenate = "0.7.3"
24 | entities = "1.0.1"
25 | paragraph-breaker = "0.4.4"
26 | xi-unicode = "0.3.0"
27 | septem = "1.1.0"
28 | byteorder = "1.5.0"
29 | flate2 = "1.0.35"
30 | levenshtein = "1.0.5"
31 | nix = { version = "0.29.0", features = ["fs", "ioctl"] }
32 | indexmap = { version = "2.6.0", features = ["serde"] }
33 | anyhow = "1.0.93"
34 | thiserror = "2.0.3"
35 | walkdir = "2.5.0"
36 | globset = "0.4.15"
37 | fxhash = "0.2.1"
38 | rand_core = "0.6.4"
39 | rand_xoshiro = "0.6.0"
40 | percent-encoding = "2.3.1"
41 | chrono = { version = "0.4.38", features = ["serde", "clock"], default-features = false }
42 |
--------------------------------------------------------------------------------
/crates/core/build.rs:
--------------------------------------------------------------------------------
1 | use std::env;
2 |
3 | fn main() {
4 | let target = env::var("TARGET").unwrap();
5 |
6 | // Cross-compiling for Kobo.
7 | if target == "arm-unknown-linux-gnueabihf" {
8 | println!("cargo:rustc-env=PKG_CONFIG_ALLOW_CROSS=1");
9 | println!("cargo:rustc-link-search=target/mupdf_wrapper/Kobo");
10 | println!("cargo:rustc-link-search=libs");
11 | println!("cargo:rustc-link-lib=dylib=stdc++");
12 | // Handle the Linux and macOS platforms.
13 | } else {
14 | let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
15 | match target_os.as_ref() {
16 | "linux" => {
17 | println!("cargo:rustc-link-search=target/mupdf_wrapper/Linux");
18 | println!("cargo:rustc-link-lib=dylib=stdc++");
19 | },
20 | "macos" => {
21 | println!("cargo:rustc-link-search=target/mupdf_wrapper/Darwin");
22 | println!("cargo:rustc-link-lib=dylib=c++");
23 | },
24 | _ => panic!("Unsupported platform: {}.", target_os),
25 | }
26 |
27 | println!("cargo:rustc-link-lib=mupdf-third");
28 | }
29 |
30 | println!("cargo:rustc-link-lib=z");
31 | println!("cargo:rustc-link-lib=bz2");
32 | println!("cargo:rustc-link-lib=jpeg");
33 | println!("cargo:rustc-link-lib=png16");
34 | println!("cargo:rustc-link-lib=gumbo");
35 | println!("cargo:rustc-link-lib=openjp2");
36 | println!("cargo:rustc-link-lib=jbig2dec");
37 | }
38 |
--------------------------------------------------------------------------------
/crates/core/src/battery/fake.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Error;
2 | use super::{Battery, Status};
3 |
4 | pub struct FakeBattery {
5 | capacity: f32,
6 | status: Status,
7 | }
8 |
9 | impl FakeBattery {
10 | pub fn new() -> FakeBattery {
11 | FakeBattery { capacity: 50.0, status: Status::Discharging }
12 | }
13 | }
14 |
15 | impl Battery for FakeBattery {
16 | fn capacity(&mut self) -> Result, Error> {
17 | Ok(vec![self.capacity])
18 | }
19 |
20 | fn status(&mut self) -> Result, Error> {
21 | Ok(vec![self.status])
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/crates/core/src/battery/kobo.rs:
--------------------------------------------------------------------------------
1 | use std::fs::File;
2 | use std::path::Path;
3 | use std::io::{Read, Seek, SeekFrom};
4 | use anyhow::{Error, format_err};
5 | use crate::device::CURRENT_DEVICE;
6 | use super::{Battery, Status};
7 |
8 | const BATTERY_INTERFACES: [&str; 3] = ["/sys/class/power_supply/bd71827_bat",
9 | "/sys/class/power_supply/mc13892_bat",
10 | "/sys/class/power_supply/battery"];
11 | const POWER_COVER_INTERFACE: &str = "/sys/class/misc/cilix";
12 |
13 | const BATTERY_CAPACITY: &str = "capacity";
14 | const BATTERY_STATUS: &str = "status";
15 |
16 | const POWER_COVER_CAPACITY: &str = "cilix_bat_capacity";
17 | const POWER_COVER_STATUS: &str = "charge_status";
18 | const POWER_COVER_CONNECTED: &str = "cilix_conn";
19 |
20 | pub struct PowerCover {
21 | capacity: File,
22 | status: File,
23 | connected: File,
24 | }
25 |
26 | // TODO: health, technology, time_to_full_now, time_to_empty_now
27 | pub struct KoboBattery {
28 | capacity: File,
29 | status: File,
30 | power_cover: Option,
31 | }
32 |
33 | impl KoboBattery {
34 | pub fn new() -> Result {
35 | let base = Path::new(BATTERY_INTERFACES.iter()
36 | .find(|bi| Path::new(bi).exists())
37 | .ok_or_else(|| format_err!("battery path missing"))?);
38 | let capacity = File::open(base.join(BATTERY_CAPACITY))?;
39 | let status = File::open(base.join(BATTERY_STATUS))?;
40 | let power_cover = if CURRENT_DEVICE.has_power_cover() {
41 | let base = Path::new(POWER_COVER_INTERFACE);
42 | let capacity = File::open(base.join(POWER_COVER_CAPACITY))?;
43 | let status = File::open(base.join(POWER_COVER_STATUS))?;
44 | let connected = File::open(base.join(POWER_COVER_CONNECTED))?;
45 | Some(PowerCover { capacity, status, connected })
46 | } else {
47 | None
48 | };
49 | Ok(KoboBattery { capacity, status, power_cover })
50 | }
51 | }
52 |
53 | impl KoboBattery {
54 | fn is_power_cover_connected(&mut self) -> Result {
55 | if let Some(power_cover) = self.power_cover.as_mut() {
56 | let mut buf = String::new();
57 | power_cover.connected.seek(SeekFrom::Start(0))?;
58 | power_cover.connected.read_to_string(&mut buf)?;
59 | Ok(buf.trim_end().parse::().map_or(false, |v| v == 1))
60 | } else {
61 | Ok(false)
62 | }
63 | }
64 | }
65 |
66 | impl Battery for KoboBattery {
67 | fn capacity(&mut self) -> Result, Error> {
68 | let mut buf = String::new();
69 | self.capacity.seek(SeekFrom::Start(0))?;
70 | self.capacity.read_to_string(&mut buf)?;
71 | let capacity = buf.trim_end().parse::()
72 | .unwrap_or(0.0);
73 | if matches!(self.is_power_cover_connected(), Ok(true)) {
74 | let mut buf = String::new();
75 | self.power_cover.iter_mut().for_each(|power_cover| {
76 | power_cover.capacity.seek(SeekFrom::Start(0)).ok();
77 | power_cover.capacity.read_to_string(&mut buf).ok();
78 | });
79 | let aux_capacity = buf.trim_end().parse::()
80 | .unwrap_or(0.0);
81 | Ok(vec![capacity, aux_capacity])
82 | } else {
83 | Ok(vec![capacity])
84 | }
85 | }
86 |
87 | fn status(&mut self) -> Result, Error> {
88 | let mut buf = String::new();
89 | self.status.seek(SeekFrom::Start(0))?;
90 | self.status.read_to_string(&mut buf)?;
91 | let status = match buf.trim_end() {
92 | "Discharging" => Status::Discharging,
93 | "Charging" => Status::Charging,
94 | "Not charging" | "Full" => Status::Charged,
95 | _ => Status::Unknown,
96 |
97 | };
98 | if matches!(self.is_power_cover_connected(), Ok(true)) {
99 | let mut buf = String::new();
100 | self.power_cover.iter_mut().for_each(|power_cover| {
101 | power_cover.status.seek(SeekFrom::Start(0)).ok();
102 | power_cover.status.read_to_string(&mut buf).ok();
103 | });
104 | let aux_status = match buf.trim_end().parse::() {
105 | Ok(0) => Status::Discharging,
106 | Ok(2) => Status::Charging,
107 | Ok(3) => Status::Charged,
108 | _ => Status::Unknown,
109 | };
110 | Ok(vec![status, aux_status])
111 | } else {
112 | Ok(vec![status])
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/crates/core/src/battery/mod.rs:
--------------------------------------------------------------------------------
1 | mod kobo;
2 | mod fake;
3 |
4 | use anyhow::Error;
5 |
6 | pub use self::kobo::KoboBattery;
7 | pub use self::fake::FakeBattery;
8 |
9 | #[derive(Debug, Copy, Clone, Eq, PartialEq)]
10 | pub enum Status {
11 | Discharging,
12 | Charging,
13 | Charged,
14 | Unknown
15 | // Full,
16 | }
17 |
18 | impl Status {
19 | pub fn is_wired(self) -> bool {
20 | matches!(self, Status::Charging | Status::Charged)
21 | }
22 | }
23 |
24 | pub trait Battery {
25 | fn capacity(&mut self) -> Result, Error>;
26 | fn status(&mut self) -> Result, Error>;
27 | }
28 |
--------------------------------------------------------------------------------
/crates/core/src/dictionary/errors.rs:
--------------------------------------------------------------------------------
1 | //! Errors for the Dict dictionary crate.
2 | use std::error;
3 |
4 | /// Error type, representing the errors which can be returned by the libdict library.
5 | ///
6 | /// This enum represents a handful of custom errors and wraps `io:::Error` and
7 | /// `string::FromUtf8Error`.
8 | #[derive(Debug)]
9 | pub enum DictError {
10 | /// Invalid character, e.g. within the index file; the error contains the erroneous character,
11 | /// and optionally line and position.
12 | InvalidCharacter(char, Option, Option),
13 | /// Occurs whenever a line in an index file misses a column.
14 | MissingColumnInIndex(usize),
15 | /// Invalid file format, contains an explanation an optional path to the
16 | /// file with the invalid file format.
17 | InvalidFileFormat(String, Option),
18 | /// This reports a malicious / malformed index file, which requests a buffer which is too large.
19 | MemoryError,
20 | /// This reports words which are not present in the dictionary.
21 | WordNotFound(String),
22 | /// A wrapped io::Error.
23 | IoError(::std::io::Error),
24 | /// A wrapped Utf8Error.
25 | Utf8Error(::std::string::FromUtf8Error),
26 | /// Errors thrown by the flate2 crate - not really descriptive errors, though.
27 | DeflateError(flate2::DecompressError),
28 | }
29 |
30 | impl ::std::fmt::Display for DictError {
31 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
32 | match *self {
33 | DictError::IoError(ref e) => e.fmt(f),
34 | DictError::Utf8Error(ref e) => e.fmt(f),
35 | DictError::DeflateError(ref err) => write!(f, "Error while using \
36 | the flate2 crate: {:?}", err),
37 | DictError::MemoryError => write!(f, "not enough memory available"),
38 | DictError::WordNotFound(ref word) => write!(f, "Word not found: {}", word),
39 | DictError::InvalidCharacter(ref ch, ref line, ref pos) => {
40 | let mut ret = write!(f, "Invalid character {}", ch);
41 | if let Some(ln) = *line {
42 | ret = write!(f, " on line {}", ln);
43 | }
44 | if let Some(pos) = *pos {
45 | ret = write!(f, " at position {}", pos);
46 | }
47 | ret
48 | },
49 | DictError::MissingColumnInIndex(ref lnum) => write!(f, "line {}: not \
50 | enough -separated columns found, expected at least 3", lnum),
51 | DictError::InvalidFileFormat(ref explanation, ref path) =>
52 | write!(f, "{}{}", path.clone().unwrap_or_else(String::new), explanation)
53 | }
54 | }
55 | }
56 |
57 | impl error::Error for DictError {
58 | fn source(&self) -> Option<&(dyn error::Error + 'static)> {
59 | match *self {
60 | DictError::IoError(ref err) => err.source(),
61 | DictError::Utf8Error(ref err) => err.source(),
62 | _ => None,
63 | }
64 | }
65 | }
66 |
67 | // Allow seamless coercion from::Error.
68 | impl From<::std::io::Error> for DictError {
69 | fn from(err: ::std::io::Error) -> DictError {
70 | DictError::IoError(err)
71 | }
72 | }
73 |
74 | impl From<::std::string::FromUtf8Error> for DictError {
75 | fn from(err: ::std::string::FromUtf8Error) -> DictError {
76 | DictError::Utf8Error(err)
77 | }
78 | }
79 |
80 | impl From for DictError {
81 | fn from(err: flate2::DecompressError) -> DictError {
82 | DictError::DeflateError(err)
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/crates/core/src/dictionary/testdata/case_insensitive_dict.dict:
--------------------------------------------------------------------------------
1 |
2 |
3 | 00-database-dictfmt-1.12.1
4 | 00-database-short
5 | Test Dict
6 | This file was converted from the original database on:
7 | Fri Jun 12 12:16:13 2020
8 |
9 | The original data is available from:
10 | unknown
11 |
12 | The original data was distributed with the notice shown below. No
13 | additional restrictions are claimed. Please redistribute this changed
14 | version under the same conditions and restriction that apply to the
15 | original version.
16 |
17 | foo
18 | definition
19 | Bar
20 | test for case-sensitivity
21 | あいおい
22 | test for non-latin characters
23 | straße
24 | test for non-latin case-sensitivity
25 | unknown
26 | abeforstßあいお
27 |
--------------------------------------------------------------------------------
/crates/core/src/dictionary/testdata/case_insensitive_dict.index:
--------------------------------------------------------------------------------
1 | 00-database-allchars B B
2 | 00-database-alphabet I4 U
3 | 00-database-dictfmt-1.12.1 C b
4 | 00-database-info + Fu
5 | 00-database-short d h
6 | 00-database-url Iw I
7 | 00-database-utf8 A B
8 | bar G7 e
9 | foo Gs P
10 | straße IE s
11 | あいおい HZ r
12 |
--------------------------------------------------------------------------------
/crates/core/src/dictionary/testdata/case_sensitive_dict.dict:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 00-database-dictfmt-1.12.1
5 | 00-database-short
6 | Case Sensitive Test Dict
7 | This file was converted from the original database on:
8 | Fri Jun 12 15:24:14 2020
9 |
10 | The original data is available from:
11 | unknown
12 |
13 | The original data was distributed with the notice shown below. No
14 | additional restrictions are claimed. Please redistribute this changed
15 | version under the same conditions and restriction that apply to the
16 | original version.
17 |
18 | foo
19 | definition
20 | Bar
21 | test for case-sensitivity
22 | あいおい
23 | test for non-latin characters
24 | straße
25 | test for non-latin case-sensitivity
26 | unknown
27 | Baeforstßあいお
28 |
--------------------------------------------------------------------------------
/crates/core/src/dictionary/testdata/case_sensitive_dict.index:
--------------------------------------------------------------------------------
1 | 00-database-allchars B B
2 | 00-database-alphabet JI U
3 | 00-database-case-sensitive C B
4 | 00-database-dictfmt-1.12.1 D b
5 | 00-database-info BO Fu
6 | 00-database-short e w
7 | 00-database-url JA I
8 | 00-database-utf8 A B
9 | Bar HL e
10 | foo G8 P
11 | straße IU s
12 | あいおい Hp r
13 |
--------------------------------------------------------------------------------
/crates/core/src/framebuffer/ion_sys.rs:
--------------------------------------------------------------------------------
1 | use nix::ioctl_readwrite;
2 |
3 | const MAGIC: u8 = b'I';
4 |
5 | ioctl_readwrite!(ion_alloc, MAGIC, 0, IonAllocationData);
6 | ioctl_readwrite!(ion_free, MAGIC, 1, IonHandleData);
7 | ioctl_readwrite!(ion_map, MAGIC, 2, IonFdData);
8 |
9 | pub const ION_HEAP_MASK_CARVEOUT: libc::c_uint = 4;
10 |
11 | type IonUserHandle = libc::c_int;
12 |
13 | #[repr(C)]
14 | pub struct IonAllocationData {
15 | pub len: libc::size_t,
16 | pub align: libc::size_t,
17 | pub heap_id_mask: libc::c_uint,
18 | pub flags: libc::c_uint,
19 | pub handle: IonUserHandle,
20 | }
21 |
22 | #[repr(C)]
23 | pub struct IonHandleData {
24 | pub handle: IonUserHandle,
25 | }
26 |
27 | #[repr(C)]
28 | pub struct IonFdData {
29 | pub handle: IonUserHandle,
30 | pub fd: libc::c_int,
31 | }
32 |
--------------------------------------------------------------------------------
/crates/core/src/framebuffer/linuxfb_sys.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused)]
2 |
3 | use std::mem;
4 | use std::fs::File;
5 | use std::os::unix::io::AsRawFd;
6 | use anyhow::{Error, Context};
7 | use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
8 |
9 | ioctl_read_bad!(read_variable_screen_info, FBIOGET_VSCREENINFO, VarScreenInfo);
10 | ioctl_write_ptr_bad!(write_variable_screen_info, FBIOPUT_VSCREENINFO, VarScreenInfo);
11 | ioctl_read_bad!(read_fixed_screen_info, FBIOGET_FSCREENINFO, FixScreenInfo);
12 |
13 | pub const FBIOGET_VSCREENINFO: libc::c_ulong = 0x4600;
14 | pub const FBIOPUT_VSCREENINFO: libc::c_ulong = 0x4601;
15 | pub const FBIOGET_FSCREENINFO: libc::c_ulong = 0x4602;
16 |
17 | #[repr(C)]
18 | #[derive(Clone, Debug)]
19 | pub struct FixScreenInfo {
20 | pub id: [u8; 16],
21 | pub smem_start: usize,
22 | pub smem_len: u32,
23 | pub kind: u32,
24 | pub type_aux: u32,
25 | pub visual: u32,
26 | pub xpanstep: u16,
27 | pub ypanstep: u16,
28 | pub ywrapstep: u16,
29 | pub line_length: u32,
30 | pub mmio_start: usize,
31 | pub mmio_len: u32,
32 | pub accel: u32,
33 | pub capabilities: u16,
34 | pub reserved: [u16; 2],
35 | }
36 |
37 | #[repr(C)]
38 | #[derive(Clone, Debug)]
39 | pub struct VarScreenInfo {
40 | pub xres: u32,
41 | pub yres: u32,
42 | pub xres_virtual: u32,
43 | pub yres_virtual: u32,
44 | pub xoffset: u32,
45 | pub yoffset: u32,
46 | pub bits_per_pixel: u32,
47 | pub grayscale: u32,
48 | pub red: Bitfield,
49 | pub green: Bitfield,
50 | pub blue: Bitfield,
51 | pub transp: Bitfield,
52 | pub nonstd: u32,
53 | pub activate: u32,
54 | pub height: u32,
55 | pub width: u32,
56 | pub accel_flags: u32,
57 | pub pixclock: u32,
58 | pub left_margin: u32,
59 | pub right_margin: u32,
60 | pub upper_margin: u32,
61 | pub lower_margin: u32,
62 | pub hsync_len: u32,
63 | pub vsync_len: u32,
64 | pub sync: u32,
65 | pub vmode: u32,
66 | pub rotate: u32,
67 | pub colorspace: u32,
68 | pub reserved: [u32; 4],
69 | }
70 |
71 | #[repr(C)]
72 | #[derive(Clone, Debug)]
73 | pub struct Bitfield {
74 | pub offset: u32,
75 | pub length: u32,
76 | pub msb_right: u32,
77 | }
78 |
79 | impl Default for Bitfield {
80 | fn default() -> Self {
81 | unsafe { mem::zeroed() }
82 | }
83 | }
84 |
85 | impl Default for VarScreenInfo {
86 | fn default() -> Self {
87 | unsafe { mem::zeroed() }
88 | }
89 | }
90 |
91 | impl Default for FixScreenInfo {
92 | fn default() -> Self {
93 | unsafe { mem::zeroed() }
94 | }
95 | }
96 |
97 | pub fn fix_screen_info(file: &File) -> Result {
98 | let mut info: FixScreenInfo = Default::default();
99 | let result = unsafe {
100 | read_fixed_screen_info(file.as_raw_fd(), &mut info)
101 | };
102 | match result {
103 | Err(e) => Err(Error::from(e).context("can't get fixed screen info")),
104 | _ => Ok(info),
105 | }
106 | }
107 |
108 | pub fn var_screen_info(file: &File) -> Result {
109 | let mut info: VarScreenInfo = Default::default();
110 | let result = unsafe {
111 | read_variable_screen_info(file.as_raw_fd(), &mut info)
112 | };
113 | match result {
114 | Err(e) => Err(Error::from(e).context("can't get variable screen info")),
115 | _ => Ok(info),
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/crates/core/src/framebuffer/sunxi_sys.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused)]
2 |
3 | use std::mem::ManuallyDrop;
4 | use nix::{ioctl_readwrite_bad, ioctl_write_ptr_bad};
5 | ioctl_readwrite_bad!(send_update, DISP_EINK_UPDATE2, SunxiDispEinkUpdate2);
6 | ioctl_write_ptr_bad!(wait_for_update, DISP_EINK_WAIT_FRAME_SYNC_COMPLETE, SunxiDispEinkWaitFrameSyncComplete);
7 |
8 | pub const DISP_EINK_UPDATE2: libc::c_ulong = 0x0406;
9 | pub const DISP_EINK_WAIT_FRAME_SYNC_COMPLETE: libc::c_ulong = 0x4014;
10 |
11 | pub const LAYER_MODE_BUFFER: libc::c_int = 0;
12 | pub const DISP_FORMAT_8BIT_GRAY: libc::c_int = 0x50;
13 | pub const DISP_GBR_F: libc::c_int = 0x200;
14 | pub const DISP_BF_NORMAL: libc::c_int = 0;
15 | pub const DISP_SCAN_PROGRESSIVE: libc::c_int = 0;
16 | pub const DISP_EOTF_GAMMA22: libc::c_int = 0x004;
17 |
18 | #[repr(C)]
19 | #[derive(Debug)]
20 | pub struct AreaInfo {
21 | pub x_top: libc::c_uint,
22 | pub y_top: libc::c_uint,
23 | pub x_bottom: libc::c_uint,
24 | pub y_bottom: libc::c_uint,
25 | }
26 |
27 | #[repr(C)]
28 | #[derive(Debug)]
29 | pub struct DispRect {
30 | pub x: libc::c_int,
31 | pub y: libc::c_int,
32 | pub width: libc::c_uint,
33 | pub height: libc::c_uint,
34 | }
35 |
36 | #[repr(C)]
37 | pub struct DispRect64 {
38 | pub x: libc::c_longlong,
39 | pub y: libc::c_longlong,
40 | pub width: libc::c_longlong,
41 | pub height: libc::c_longlong,
42 | }
43 |
44 | #[repr(C)]
45 | #[derive(Debug)]
46 | pub struct DispRectsz {
47 | pub width: libc::c_uint,
48 | pub height: libc::c_uint,
49 | }
50 |
51 | #[repr(C)]
52 | pub struct DispLayerConfig2 {
53 | pub info: DispLayerInfo2,
54 | pub enable: bool,
55 | pub channel: libc::c_uint,
56 | pub layer_id: libc::c_uint,
57 | }
58 |
59 | #[repr(C)]
60 | pub struct DispAtwInfo {
61 | pub used: bool,
62 | pub mode: libc::c_int,
63 | pub b_row: libc::c_uint,
64 | pub b_col: libc::c_uint,
65 | pub colf_fd: libc::c_int,
66 | }
67 |
68 | #[repr(C)]
69 | pub struct DispLayerInfo2 {
70 | pub mode: libc::c_int,
71 | pub zorder: libc::c_uchar,
72 | pub alpha_mode: libc::c_uchar,
73 | pub alpha_value: libc::c_uchar,
74 | pub screen_win: DispRect,
75 | pub b_trd_out: bool,
76 | pub out_trd_mode: libc::c_int,
77 | pub color_fb: ColorFb,
78 | pub id: libc::c_uint,
79 | pub atw: DispAtwInfo,
80 | }
81 |
82 | #[repr(C)]
83 | pub struct DispFbInfo2 {
84 | pub fd: libc::c_int,
85 | pub y8_fd: libc::c_int,
86 | pub size: [DispRectsz; 3],
87 | pub align: [libc::c_uint; 3],
88 | pub format: libc::c_int,
89 | pub color_space: libc::c_int,
90 | pub trd_right_fd: libc::c_int,
91 | pub pre_multiply: bool,
92 | pub crop: DispRect64,
93 | pub flags: libc::c_int,
94 | pub scan: libc::c_int,
95 | pub eotf: libc::c_int,
96 | pub depth: libc::c_int,
97 | pub fbd_en: libc::c_uint,
98 | pub metadata_fd: libc::c_int,
99 | pub metadata_size: libc::c_uint,
100 | pub metadata_flag: libc::c_uint,
101 | }
102 |
103 | #[repr(C)]
104 | pub union ColorFb {
105 | pub color: libc::c_uint,
106 | pub fb: ManuallyDrop,
107 | }
108 |
109 | #[repr(C)]
110 | #[derive(Clone, Debug)]
111 | pub struct SunxiDispEinkUpdate2 {
112 | pub area: *const AreaInfo,
113 | pub layer_num: libc::c_ulong,
114 | pub update_mode: libc::c_ulong,
115 | pub lyr_cfg2: *const DispLayerConfig2,
116 | pub frame_id: *mut libc::c_uint,
117 | pub rotate: *const u32,
118 | pub cfa_use: libc::c_ulong,
119 | }
120 |
121 | #[repr(C)]
122 | #[derive(Clone, Debug)]
123 | pub struct SunxiDispEinkWaitFrameSyncComplete {
124 | pub frame_id: u32,
125 | }
126 |
127 | pub const EINK_INIT_MODE:u32 = 0x01;
128 | pub const EINK_DU_MODE:u32 = 0x02;
129 | pub const EINK_GC16_MODE:u32 = 0x04;
130 | pub const EINK_GC4_MODE:u32 = 0x08;
131 | pub const EINK_A2_MODE:u32 = 0x10;
132 | pub const EINK_GL16_MODE:u32 = 0x20;
133 | pub const EINK_GLR16_MODE:u32 = 0x40;
134 | pub const EINK_GLD16_MODE:u32 = 0x80;
135 | pub const EINK_GU16_MODE:u32 = 0x84;
136 | pub const EINK_GCK16_MODE:u32 = 0x90;
137 | pub const EINK_GLK16_MODE:u32 = 0x94;
138 | pub const EINK_CLEAR_MODE:u32 = 0x88;
139 | pub const EINK_GC4L_MODE:u32 = 0x8c;
140 | pub const EINK_GCC16_MODE:u32 = 0xa0;
141 |
142 | pub const EINK_AUTO_MODE:u32 = 0x0000_8000;
143 | pub const EINK_DITHERING_Y1:u32 = 0x0180_0000;
144 | pub const EINK_DITHERING_Y4:u32 = 0x0280_0000;
145 | pub const EINK_DITHERING_SIMPLE:u32 = 0x0480_0000;
146 | pub const EINK_DITHERING_NTX_Y1:u32 = 0x0880_0000;
147 |
148 | pub const EINK_GAMMA_CORRECT:u32 = 0x0020_0000;
149 | pub const EINK_MONOCHROME:u32 = 0x0040_0000;
150 | pub const EINK_NEGATIVE_MODE:u32 = 0x0001_0000;
151 | pub const EINK_REGAL_MODE:u32 = 0x0008_0000;
152 | pub const EINK_NO_MERGE:u32 = 0x8000_0000;
153 |
154 | pub const EINK_PARTIAL_MODE:u32 = 0x0400;
155 |
--------------------------------------------------------------------------------
/crates/core/src/framebuffer/transform.rs:
--------------------------------------------------------------------------------
1 | use lazy_static::lazy_static;
2 | use super::image::Pixmap;
3 | use crate::color::Color;
4 |
5 | pub type ColorTransform = fn(u32, u32, Color) -> Color;
6 |
7 | const DITHER_PITCH: u32 = 128;
8 |
9 | lazy_static! {
10 | // Tileable blue noise matrix.
11 | pub static ref DITHER_G16_DRIFTS: Vec = {
12 | let pixmap = Pixmap::from_png("resources/blue_noise-128.png").unwrap();
13 | // The gap between two succesive colors in G16 is 17.
14 | // Map {0 .. 255} to {-8 .. 8}.
15 | pixmap.data().iter().map(|&v| {
16 | match v {
17 | 0..=119 => v as i8 / 15 - 8,
18 | 120 => 0,
19 | 121..=255 => ((v - 121) / 15) as i8,
20 | }
21 | }).collect()
22 | };
23 |
24 | // Tileable blue noise matrix.
25 | pub static ref DITHER_G2_DRIFTS: Vec = {
26 | let pixmap = Pixmap::from_png("resources/blue_noise-128.png").unwrap();
27 | // Map {0 .. 255} to {-128 .. 127}.
28 | pixmap.data().iter().map(|&v| {
29 | match v {
30 | 0..=127 => -128 + (v as i8),
31 | 128..=255 => (v - 128) as i8,
32 | }
33 | }).collect()
34 | };
35 | }
36 |
37 | // Ordered dithering.
38 | // The input color is in {0 .. 255}.
39 | // The output color is in G16.
40 | // G16 := {17 * i | i ∈ {0 .. 15}}.
41 | pub fn transform_dither_g16(x: u32, y: u32, color: Color) -> Color {
42 | let gray = color.gray();
43 | // Get the address of the drift value.
44 | let addr = (x % DITHER_PITCH) + (y % DITHER_PITCH) * DITHER_PITCH;
45 | // Apply the drift to the input color.
46 | let c = (gray as i16 + DITHER_G16_DRIFTS[addr as usize] as i16).clamp(0, 255);
47 | // Compute the distance to the previous color in G16.
48 | let d = c % 17;
49 | // Return the nearest color in G16.
50 | Color::Gray(if d < 9 {
51 | (c - d) as u8
52 | } else {
53 | (c + (17 - d)) as u8
54 | })
55 | }
56 |
57 | // Ordered dithering.
58 | // The input color is in {0 .. 255}.
59 | // The output color is in {0, 255}.
60 | pub fn transform_dither_g2(x: u32, y: u32, color: Color) -> Color {
61 | let gray = color.gray();
62 | // Get the address of the drift value.
63 | let addr = (x % DITHER_PITCH) + (y % DITHER_PITCH) * DITHER_PITCH;
64 | // Apply the drift to the input color.
65 | let c = (gray as i16 + DITHER_G2_DRIFTS[addr as usize] as i16).clamp(0, 255);
66 | // Return the nearest color in G2.
67 | Color::Gray(if c < 128 {
68 | 0
69 | } else {
70 | 255
71 | })
72 | }
73 |
74 | pub fn transform_identity(_x: u32, _y: u32, color: Color) -> Color {
75 | color
76 | }
77 |
--------------------------------------------------------------------------------
/crates/core/src/frontlight/mod.rs:
--------------------------------------------------------------------------------
1 | mod standard;
2 | mod natural;
3 | mod premixed;
4 |
5 | use serde::{Serialize, Deserialize};
6 | pub use self::standard::StandardFrontlight;
7 | pub use self::natural::NaturalFrontlight;
8 | pub use self::premixed::PremixedFrontlight;
9 | use crate::geom::lerp;
10 |
11 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
12 | pub struct LightLevels {
13 | pub intensity: f32,
14 | pub warmth: f32,
15 | }
16 |
17 | impl Default for LightLevels {
18 | fn default() -> Self {
19 | LightLevels {
20 | intensity: 0.0,
21 | warmth: 0.0,
22 | }
23 | }
24 | }
25 |
26 | impl LightLevels {
27 | pub fn interpolate(self, other: Self, t: f32) -> Self {
28 | LightLevels {
29 | intensity: lerp(self.intensity, other.intensity, t),
30 | warmth: lerp(self.warmth, other.warmth, t),
31 | }
32 | }
33 | }
34 |
35 | pub trait Frontlight {
36 | // value is a percentage.
37 | fn set_intensity(&mut self, value: f32);
38 | fn set_warmth(&mut self, value: f32);
39 | fn levels(&self) -> LightLevels;
40 | }
41 |
42 | impl Frontlight for LightLevels {
43 | fn set_intensity(&mut self, value: f32) {
44 | self.intensity = value;
45 | }
46 |
47 | fn set_warmth(&mut self, value: f32) {
48 | self.warmth = value;
49 | }
50 |
51 | fn levels(&self) -> LightLevels {
52 | *self
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/crates/core/src/frontlight/premixed.rs:
--------------------------------------------------------------------------------
1 | use std::path::Path;
2 | use std::io::Write;
3 | use std::fs::File;
4 | use std::fs::OpenOptions;
5 | use anyhow::Error;
6 | use crate::device::CURRENT_DEVICE;
7 | use super::{Frontlight, LightLevels};
8 |
9 | const FRONTLIGHT_WHITE: &str = "/sys/class/backlight/mxc_msp430.0/brightness";
10 |
11 | // Forma
12 | const FRONTLIGHT_ORANGE_A: &str = "/sys/class/backlight/tlc5947_bl/color";
13 | // Libra H₂O, Clara HD, Libra 2, Clara BW, Libra Colour, Clara Colour
14 | const FRONTLIGHT_ORANGE_B: &str = "/sys/class/backlight/lm3630a_led/color";
15 | // Sage, Libra 2, Clara 2E, Elipsa 2E
16 | const FRONTLIGHT_ORANGE_C: &str = "/sys/class/leds/aw99703-bl_FL1/color";
17 |
18 | pub struct PremixedFrontlight {
19 | intensity: f32,
20 | warmth: f32,
21 | white: File,
22 | orange: File,
23 | }
24 |
25 | impl PremixedFrontlight {
26 | pub fn new(intensity: f32, warmth: f32) -> Result {
27 | let white = OpenOptions::new().write(true).open(FRONTLIGHT_WHITE)?;
28 | let orange_path = if Path::new(FRONTLIGHT_ORANGE_C).exists() {
29 | FRONTLIGHT_ORANGE_C
30 | } else if Path::new(FRONTLIGHT_ORANGE_B).exists() {
31 | FRONTLIGHT_ORANGE_B
32 | } else {
33 | FRONTLIGHT_ORANGE_A
34 | };
35 | let orange = OpenOptions::new().write(true).open(orange_path)?;
36 | Ok(PremixedFrontlight { intensity, warmth, white, orange })
37 | }
38 | }
39 |
40 | impl Frontlight for PremixedFrontlight {
41 | fn set_intensity(&mut self, intensity: f32) {
42 | let white = intensity.round() as i16;
43 | write!(self.white, "{}", white).unwrap();
44 | self.intensity = intensity;
45 | }
46 |
47 | fn set_warmth(&mut self, warmth: f32) {
48 | let mut orange = (warmth / 10.0).round() as i16;
49 | if CURRENT_DEVICE.mark() != 8 {
50 | orange = 10 - orange;
51 | }
52 | write!(self.orange, "{}", orange).unwrap();
53 | self.warmth = warmth;
54 | }
55 |
56 | fn levels(&self) -> LightLevels {
57 | LightLevels {
58 | intensity: self.intensity,
59 | warmth: self.warmth,
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/crates/core/src/frontlight/standard.rs:
--------------------------------------------------------------------------------
1 | use std::fs::File;
2 | use std::fs::OpenOptions;
3 | use std::os::unix::io::AsRawFd;
4 | use nix::ioctl_write_int_bad;
5 | use anyhow::Error;
6 | use super::{Frontlight, LightLevels};
7 |
8 | ioctl_write_int_bad!(write_frontlight_intensity, 241);
9 | const FRONTLIGHT_INTERFACE: &str = "/dev/ntx_io";
10 |
11 | pub struct StandardFrontlight {
12 | value: f32,
13 | interface: File,
14 | }
15 |
16 | impl StandardFrontlight {
17 | pub fn new(value: f32) -> Result {
18 | let interface = OpenOptions::new().write(true)
19 | .open(FRONTLIGHT_INTERFACE)?;
20 | Ok(StandardFrontlight { value, interface })
21 | }
22 | }
23 |
24 | impl Frontlight for StandardFrontlight {
25 | fn set_intensity(&mut self, value: f32) {
26 | let ret = unsafe {
27 | write_frontlight_intensity(self.interface.as_raw_fd(),
28 | value as libc::c_int)
29 | };
30 | if ret.is_ok() {
31 | self.value = value;
32 | }
33 | }
34 |
35 | fn set_warmth(&mut self, _value: f32) { }
36 |
37 | fn levels(&self) -> LightLevels {
38 | LightLevels {
39 | intensity: self.value,
40 | warmth: 0.0,
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/crates/core/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[macro_use] pub mod geom;
2 |
3 | mod unit;
4 | pub mod color;
5 | pub mod device;
6 | pub mod framebuffer;
7 | pub mod frontlight;
8 | pub mod lightsensor;
9 | pub mod battery;
10 | pub mod input;
11 | pub mod helpers;
12 | mod dictionary;
13 | pub mod document;
14 | pub mod library;
15 | pub mod view;
16 | pub mod metadata;
17 | pub mod rtc;
18 | pub mod settings;
19 | pub mod font;
20 | pub mod context;
21 | pub mod gesture;
22 |
23 | pub use anyhow;
24 | pub use fxhash;
25 | pub use chrono;
26 | pub use globset;
27 | pub use walkdir;
28 | pub use rand_core;
29 | pub use rand_xoshiro;
30 | pub use serde;
31 | pub use serde_json;
32 | pub use png;
33 |
--------------------------------------------------------------------------------
/crates/core/src/lightsensor/kobo.rs:
--------------------------------------------------------------------------------
1 | use std::io::{Read, Seek, SeekFrom};
2 | use std::fs::File;
3 | use anyhow::Error;
4 | use super::LightSensor;
5 |
6 | // The Aura ONE uses a Silicon Graphics light sensor,
7 | // the model code is si114x (where x is 5, 6, or 7).
8 | const VISIBLE_PHOTODIODE: &str = "/sys/devices/virtual/input/input3/als_vis_data";
9 |
10 | pub struct KoboLightSensor(File);
11 |
12 | impl KoboLightSensor {
13 | pub fn new() -> Result {
14 | let file = File::open(VISIBLE_PHOTODIODE)?;
15 | Ok(KoboLightSensor(file))
16 | }
17 | }
18 |
19 | impl LightSensor for KoboLightSensor {
20 | fn level(&mut self) -> Result {
21 | let mut buf = String::new();
22 | self.0.seek(SeekFrom::Start(0))?;
23 | self.0.read_to_string(&mut buf)?;
24 | let value = buf.trim_end().parse()?;
25 | Ok(value)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/crates/core/src/lightsensor/mod.rs:
--------------------------------------------------------------------------------
1 | mod kobo;
2 |
3 | use anyhow::Error;
4 |
5 | pub use self::kobo::KoboLightSensor;
6 |
7 | pub trait LightSensor {
8 | fn level(&mut self) -> Result;
9 | }
10 |
11 | impl LightSensor for u16 {
12 | fn level(&mut self) -> Result {
13 | Ok(*self)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/crates/core/src/rtc.rs:
--------------------------------------------------------------------------------
1 | use std::mem;
2 | use std::fs::File;
3 | use std::path::Path;
4 | use std::os::unix::io::AsRawFd;
5 | use anyhow::Error;
6 | use nix::{ioctl_read, ioctl_write_ptr, ioctl_none};
7 | use chrono::{Duration, Utc, Datelike, Timelike};
8 |
9 | ioctl_read!(rtc_read_alarm, b'p', 0x10, RtcWkalrm);
10 | ioctl_write_ptr!(rtc_write_alarm, b'p', 0x0f, RtcWkalrm);
11 | ioctl_none!(rtc_disable_alarm, b'p', 0x02);
12 |
13 | #[repr(C)]
14 | #[derive(Debug, Clone)]
15 | pub struct RtcTime {
16 | tm_sec: libc::c_int,
17 | tm_min: libc::c_int,
18 | tm_hour: libc::c_int,
19 | tm_mday: libc::c_int,
20 | tm_mon: libc::c_int,
21 | tm_year: libc::c_int,
22 | tm_wday: libc::c_int,
23 | tm_yday: libc::c_int,
24 | tm_isdst: libc::c_int,
25 | }
26 |
27 | impl Default for RtcWkalrm {
28 | fn default() -> Self {
29 | unsafe { mem::zeroed() }
30 | }
31 | }
32 |
33 | #[repr(C)]
34 | #[derive(Debug, Clone)]
35 | pub struct RtcWkalrm {
36 | enabled: libc::c_uchar,
37 | pending: libc::c_uchar,
38 | time: RtcTime,
39 | }
40 |
41 | impl RtcTime {
42 | fn year(&self) -> i32 {
43 | 1900 + self.tm_year as i32
44 | }
45 | }
46 |
47 | impl RtcWkalrm {
48 | pub fn enabled(&self) -> bool {
49 | self.enabled == 1
50 | }
51 |
52 | pub fn year(&self) -> i32 {
53 | self.time.year()
54 | }
55 | }
56 |
57 | pub struct Rtc(File);
58 |
59 | impl Rtc {
60 | pub fn new>(path: P) -> Result