KOReader na čítačke Amazon Kindle - inštalácia, skúsenosti
Dnešný článok bude venovaný open source čítačke KOReader. V článku sa venujem inštalácii, používaniu a porovnaniu so štandardnou čítačkou na Amazon Kindle Paperwhite 2 (drvivá väčšina platí aj pre ostatné čítačky od Amazonu). Ďalej sa venujem optimalizácii spotrebovanej RAM a riešeniu problémov pri spustení bez Kindle Framweorku. Článok uzatváram trochou disassembleru a malým pluginom pre lepšiu podporu Kindle.
Vstavaná čítačka od Amazonu je pomerne spoľahlivá, ale má minimálne možnosti nastavenia, text nezobrazuje práve najlepšie a človek občas narazí na nejaký nepríjemný bug, napríklad, že si čítačka nepamätá stranu pri použití vlastných fontov.
Inštalácia KOReadera
Po jailbreaku a rozbehaní KUAL je inštalácia extrémne jednoduchá. Stačí stiahnuť balík pre Kindle a skopírovať adresár koreader
a obsah adresára extensions
do čítačky. Teraz stačí už len spustiť KUAL menu a ťuknúť na KOReader.
Výhody vstavanej čítačky
Vstavaná čítačka je predinštalovaná v systéme. Používateľ dostane aký-taký základ bez nutnosti čokoľvek nastavovať, alebo riešiť. Knihy sa dajú bez problémov skopírovať cez USB mass storage (aj keď je nutná konverzia do podporovaného formátu).
Knižnica je užívateľsky prívetivá. Knihy môžu byť organizované do zbierok, dajú sa filtrovať a vyhľadávať. Užívateľ vôbec nevidí adresárovú štruktúru. Domovská stránka zobrazuje štandardne posledné otvorené knihy.
Zobrazenie kníh nie je úplne najlepšie. Text sa renderuje strašne. Čo môžem jemne pochváliť rýchlosť zoomu a posunu v PDF dokumentoch.
V čom je iný KOReader
Existuje mnoho dôvodov, prečo by niekto mohol chcieť používať alternatívnu čítačku. V tejto sekcii skúsim zhrnúť zmeny oproti vstavanej čítačke, výhody a nevýhody.
Zobrazenie knihy
Začnem pre mňa najdôležitejším bodom - zobrazenie knihy. Kvalita (alebo nekvalita) sadzby textu dokáže ovplyvniť rýchlosť a celkový dojem z čítania. V nasledujúcom texte porovnávam zobrazenie textu v oboch čítačkách.
Delenie slov
Teoreticky delenie slov podporujú obe čítačky. Kindle však vyžaduje vloženie znaku soft hyphen na každé miesto, kde sa dá slovo rozdeliť. KOReader má vstavané slovníky, takže ak má kniha správne nastavený jazyk, dokáže rozdeliť slová automaticky.
Okrem delenia slov sa dajú v KOReaderi nastaviť aj veľkosti medzier medzi slovami a pravidlá pre minimálnu vzdialenosť slov (redukciu medzier). Vo výsledku je KOReader schopný udržať aj pri pomerne krátkych riadkoch konzistentnú veľkosť medzier. U Kindle je naopak rozdiel medzi štandardnou medzerou (riadok hypothetical questions) a najhorším riadkom (scientists …) obrovský.
Mimochodom soft hyphen sa dá pridať v calibre cez dialóg Polish books (klávesová skratka p
) a výber Add soft hyphens.
Kerning
Medzery medzi znakmi by mali mať konzistentnú veľkosť. Pri výpočte šírky medzery sa musí brať do úvahy tvar písmen. Napríklad písmena AV
by mali byť k sebe bližšie než je ich typická šírka, pretože ich tvar do seba pekne zapadne. Fonty majú v sebe zvyčajne tabuľku obsahujúcu horizontálny posun znakov oproti štandardnému posunu pre každú dvojicu znakov, ktorá do seba nejak zapadne. Kindle v podpore kerningu absolútne zlyháva.
Teraz sa pozrime na príklad kerningu s rovnakým písmom. Do pozornosti dávam napríklad dvojicu ou
v slove out, hy
v slove physics, alebo nt
v slove eventually.
Ligatúry
Niektoré skupiny znakov sa môžu zobrazovať ako jeden znak. Typický príklad je dvojica fi
, kde sa vrchná časť znaku f
prekrýva s bodkou nad znakom i
. KOReader pekne podporuje ligatúry, ale Kindle zase absolútne bez podpory.
Hinting
Ako na poslednú vlastnosť zobrazenia textu sa pozrieme na hinting. Ide o úpravu kontrolných bodov fontu tak, aby sa presne zarovnali do mriežky pixelov. Horizontálne a vertikálne hrany sú vďaka tomu ostrejšie. Zmenu pozície bodov riadi bytekód, ktorý sa interpretuje. Niektoré fonty sú navrhnuté tak, že bez hintingu sa nezobrazujú korektne. Opäť Kindle nemá žiadnu podporu hintingu. V čítačke KOReader sa dá hinting voliteľne zapnúť.
Stránkovanie
Obe čítačky majú iný algoritmus stránkovania. Kindle považuje za stránku určitý pevne daný počet znakov. Zmena veľkosti, alebo typu písma teda nemení počet stránok. Pri čítaní podľa nastavenej veľkosti písma sa niekedy zmení strana o 2 čísla, inokedy o 0.
KOReader čísluje omnoho presnejšie, ale zároveň to znamená, že pri zmene písma sa musí viac-menej vyrenderovať celá kniha. Kindle preto vie meniť veľkosť písma prakticky okamžite, pretože odhad počtu stránok sa pri tejto operácii nemení.
Fonty
Ako som na začiatku spomenul, Kindle nefunguje dobre s inými fontmi než so vstavanými kvôli bugu, pri ktorom si napamätá aktuálnu pozíciu. Zo vstavaných fontov sa mi najlepšie číta Bookerly, ale italic variant sa číta hrozne.
Celkovo mám pocit, že keď je italic šikmý, tak sa na e-inku nerenderuje práve najlepšie. Práve z toho dôvodu používam písmo Literata, ktoré má italic takmer rovný, ale z tvaru znakov sa dá ľahko odlíšiť od normálneho písma. Škoda, že vo vstavanej čítačke je prakticky nepoužiteľný kvôli bugom.
Na Kindli môžem v prípade podpory PDF pochváliť len rýchlosť zoomu. Okrem nastavenia kontrastu nemá Kindle žiadne špeciálne nastavenia pre PDF.
KOReader síce nevie rýchly zoom, má implementovaný reflow a dokonca OCR (tesseract OCR engine). Výber textu a slovníky by mali fungovať aj na naskenovaných dokumentoch.
Knižnica
Pre KOReader je knižnica len obyčajným adresárom. Na rozdiel od Kindle neindexuje na pozadí knihy a nezobrazí všetky naraz.
Nie som náročný na funkcie knižnice, takže to, čo dokáže KOReader mi úplne postačuje. Knižnicu je možné zobrazovať ako zoznam, alebo mriežku s náhľadmi. Zoradenie je možné podľa abecedy, dátumu vytvorenia, dátumu posledného čítania, alebo podľa priebehu čítania. V zozname je možné zobraziť aj sériu kníh a poradové číslo v sérii.
Ovládanie a nastavenia
V čítačke Kindle sa dá nastaviť zobrazenie knižnice (mriežka, zoznam), veľkosť, hrúbka a typ fontu, riadkovanie, zarovnanie a čiastočne intenzita osvetlenia. To je asi všetko, čo sa dá v Kindle nastaviť. Napriek tomu občas ťuknem na nesprávnu ikonku. Ovládanie by mohlo byť vážne intuitívnejšie.
Na prvom obrázku (vľavo hore) je menu settings. Tu nájdeme režim lietadla, okamžitú synchronizáciu (od kedy je okamžitá synchronizácia nastavením?), tlačidlo všetkých nastavení a nastavenie jasu. Na tom istom screenshote je v dolnej časti zobrazená aktuálna kapitola a pod ňou tlačidlá na rýchlu navigáciu v dokumente. Tie tlačidlá vyzerajú ako prepínač zobrazenia, ale v skutočnosti jedno zobrazí popup s prehliadačom stránky a druhé mriežku stránok (v tomto dokumente nedostupné). Z pohľadu UX sú tie 2 tlačidlá absolútna katastrofa, pretože vyzerajú ako prepínač. Ešte horšie je, že keď človek chce rýchlo listovať knihou, musí vliezť do nastavení, ktoré ani nie sú nastaveniami. Nedávno som chcel v jednej knihe nalistovať graf a nepamätal som si presne kde bol a trvalo mi 20(!) minút kým som našiel prehliadač stránok (pamätal som si, že niekde musí byť, ale nevedel som kde).
KOReader má nastavenia rozčlenené do horného menu (vyvolané potiahnutím zhora, alebo kliknutím v hornej časti) a dolného menu (vyvolané potiahnutím zdola, alebo kliknutím). Pred použitím nastavení odporúčam prečítať si príručku.
Predpokladám, že si nikto moju radu aj tak nezobral k srdcu, takže pre istotu spomeniem s čím som mal najväčší problém. KOReader má samostatné nastavenia pre každú knihu. Pri každej knihe si môžem nastaviť vlastnú veľkosť písma, typ fontu, štýly atď. Nastavenia budú platiť len pre danú knihu. Ak v Kindle niečo nastavím, platí to všade. Ak to urobím v KOReaderi, platí to len pre danú knihu.
Okrem nastavení knihy existujú aj defaultné nastavenia. Tie sa prekopírujú do nastavení knihy po prvom otvorení knihy. Ďalšia zmena defaultných nastavení neovplyvní už otvorenú knihu. Aby sa prejavila zmena defaultných nastavení v už otvorenej knihe, je potrebné otvoriť horné menu, kliknúť na ikonu dokumentu a vybrať položku menu Reset document settings to default.
Zostáva už len otázka, ako zmeniť defaultné nastavenia. Tie sa menia dlhým podržaním prsta na voľbe, ktorú chcem nastaviť ako default.
Toto ovládanie nie je intuitívne, pretože bez manuálu užívateľ nemá ako vedieť, že defaultné nastavenia zmení podržaním prsta. Tak isto rozdelenie medzi defaultné nastavenia a nastavenia knihy nie je intuitívne, pretože užívateľ jednoducho čaká, že sa nastavenia prejavia všade.
Že niečo nie je intuitívne ešte neznamená, že je to zlé. Ani Blender nie je intuitívny, ale vďaka takému ovládaniu, aké má je v rukách profesionála extrémne efektívny. Ja musím KOReader preto pochváliť za tieto rozhodnutia, pretože občas narazím na knihu, kde autor nastavil malé písmo, inde naopak príliš veľké a preto sa mi hodí nastaviť veľkosť písma špecificky pre vybranú knihu. Na ovládanie som si zvykol veľmi rýchlo, takže tých 5 minút čítania užívateľskej príručky stálo za to.
Spodné menu je jednoduché, ľahko použiteľné a sprístupňuje rôzne nastavenia zobrazenia dokumentu. Podržaním prsta na názve možnosti sa vyvolá pomoc k danej položke (obrázok vľavo dole). Niektoré nastavenia majú tlačidlo troch bodiek, ktorým sa vyvolá dialóg pre zadanie presnej hodnoty (obrázok hore v strede).
V hornom menu by som chcel upozorniť na položku Taps and gestures. V tejto sekcii je možné priradiť rôzne akcie gestám ako ťahanie prstom pri niektorom okraji, ťahanie dvoma prstami, kliknutie na niektorý z rohov …
Členenie menu nie je ani zďaleka ideálne. Za bežných okolností by som určite skritizoval umiestnenie osvetlenia niekam do horného menu. Našťastie je tu možnosť nastaviť gestá. Momentálne mám nastavenú hodnotu osvetlenia, ktorú používam v noci a akciu zapnutie / vypnutie osvetlenia mám priradenú na ťuknutie vľavo dole. Okrem toho zvýšenie a zníženie jasu mám priradené na potiahnutie 2 prstami, takže nemusím vôbec chodiť do menu.
Pri menu by som sa ešte spomenul katastrofálne zobrazenie položiek (dolný rad, stredný obrázok). Šípky vpravo pri položkách by mali byť zarovnané na pravý okraj, aby zbytočne nerušili text. Čo je horšie je šípka hore v poslednom riadku menu - to je tlačidlo späť.
V spodnej časti je konfigurovateľný status bar. Nastaviť sa dá formát položiek, zoznam položiek, skrývanie ak sú niektoré irelevantné. Dá sa nastaviť napríklad zobrazenie stavu batérie ak je nabitie pod určitou hodnotou, alebo zobraziť ikonu ak je zapnuté osvetlenie (cez deň nemám šancu vidieť, že mám zapnuté osvetlenie, ale ikonu si bez problémov všimnem a vypnem ho keď je zbytočné).
Open source
Zdrojové kódy sú dostupné na githube. Renderovacie jadro je z čítačky Cool Reader. Čítačka sa dá veľmi jednoducho skompilovať a spustiť na desktope (stačí klonovať git clone --recursive https://github.com/koreader/koreader.git
a spustiť ./kodev run
v adresári koreader
).
Užívateľské rozhranie je naprogramované v jazyku lua. Väčšina úprav (prakticky všetko, ak človek nepotrebuje upravovať renderovacie jadro) sa dá robiť bez kompilácie, stačí sa prihlásiť na čítačku cez SSH a priamo upravovať zdrojové kódy. Ak by niekto chcel vyvíjať cez SSH odporúčam:
# štandardná IP adresa ssh root@192.168.15.244 # zastavenie vstavanej čítačky stop framework # spustenie cd /mnt/us/koreader ./reader.lua
Mnoho funkcií v užívateľskom rozhraní je implementovaných ako zásuvné moduly. Ukážka zásuvného modulu, ktorý sa registruje do menu a dá sa vyvolať aj cez gestá je na githube. Ladiť sa dajú bez problémov na desktope, ako aj na čítačke cez SSH.
Integrácia s Calibre
So vstavanou čítačkou Kindle pracuje calibre bez dodatočných nastavení. Calibre ju automaticky pri pripojení kábla primontuje (je to mass storage) a nastaví príslušný profil v calibre. Štandardne sa dokumenty nahrávajú do adresára documents
v dosť šialenej štruktúre autor/kniha.azw3
. Adresárová štruktúra však nie je vôbec dôležitá, pretože Kindle zobrazuje všetky knihy v jednom veľkom zozname.
Z funkcií calibre som s Kindlom používal len jednosmerné nahrávanie kníh a konverziu do AZW3. V minulosti som sa pokúšal o synchronizáciu priebehu, ale to sa mi nepodarilo, pretože nikdy som v čítačke nemal tú správnu verziu firmvéru. Nechcelo sa mi s tým vôbec babrať, pretože po aktualizácii môže byť situácia zase iná.
Kindle by sme teda mali, ale ako je na tom KOReader?
Prvým problémom, na ktorý človek narazí je absencia mass storage. Nie je to len nejaká malá drobnosť, ktorá by sa dala vyriešiť, ale je to dôsledok spustenia čítačky z partície s užívateľskými dátami. Pri mass storage musí byť partícia odpojená, čo nie je možné, kým z nej beží KOReader. Nahranie kníh je možné niekoľkými spôsobmi:
- Cez mass storage po ukončení KOReadera
- Táto možnosť by bola celkom akceptovateľná, keby vstavaná čítačka neštartovala 2-3 minúty (mimochodom KOReader sa dá spustiť aj bez ukončenia behu vstavanej čítačky, vtedy zariadeniu síce zostane menej RAM, ale človek nemusí čakať).
- Cez wifi
- Zapínať wifi na jailbreaknutom Kindle vážne nie je dobrý nápad. Amazon sa snaží všetkými prostriedkami bojovať proti jailbreaku a neexistuje spoľahlivý spôsob ako zablokovať aktualizácie.
- Cez ssh
- Osobne uprednostňujem možnosť nechať permanentne zapnutý prístup cez
ssh
a nahrávať knihy po pripojení cezsshfs
.
Z dostupných možností som si vybral podľa mňa najmenej zlú - ssh
.
Pripojenie súborového systému
Aby bolo možné použiť ssh, musí sa Kindle najskôr prepnúť do režimu USB sieťovky. Na to slúži USBNetwork hack.
Balík sa najskôr musí rozbaliť, následne sa jeden z .bin súborov (podľa podľa zariadenia) umiestní do adresára mrpackages a nakoniec sa nainštaluje cez menu KUAL pomocou položky Install MR Packages.
Po reštarte odporúčam ešte raz spustiť KUAL a v menu USBNetwork povoliť spustenie SSH po štarte.
Teraz sa bude Kindle tváriť ako sieťová karta obsluhovaná modulom jadra cdc_ether
. Keďže väčšina distribúcii používa takzvané "predictable names", bude názov sieťovky nejaká mňamka ako enp7s0f4u2i1
. Aby nebolo málo zábavy, tento názov používa aj moja dokovacia stanica, takže reálne môže dostať aj nejakú inú mňamku.
Pre krátky test môžem nastaviť IP adresu a skúsiť, či SSH funguje korektne:
ip link set enp7s0f4u2i1 up; ip addr add 192.168.15.243/24 dev enp7s0f4u2i1
Štandardná adresa, ktorú má kindle je 192.168.15.244
, takže prihlásiť sa môžem príkazom (heslo je prázdny reťazec):
$ ssh root@192.168.15.244 Welcome to Kindle! root@192.168.15.244's password: ################################################# # N O T I C E * N O T I C E * N O T I C E # ################################################# Rootfs is mounted read-only. Invoke mntroot rw to switch back to a writable rootfs. ################################################# [root@kindle root]#
Pripojenie by fungovalo, poďme teraz automatizovať pripojenie súborového systému. Začnem zistením informácií o sieťovom rozhraní zo subsystému udev
:
udevadm info -p /sys/class/net/enp7s0f4u2i1 P: /devices/pci0000:00/0000:00:08.1/0000:07:00.4/usb5/5-2/5-2:1.0/net/enp7s0f4u2i1 L: 0 E: DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:07:00.4/usb5/5-2/5-2:1.0/net/enp7s0f4u2i1 E: INTERFACE=enp7s0f4u2i1 E: IFINDEX=6 E: SUBSYSTEM=net E: USEC_INITIALIZED=18056489502 E: ID_NET_NAMING_SCHEME=v249 E: ID_NET_NAME_MAC=enxee4900000000 E: ID_NET_NAME_PATH=enp7s0f4u2 E: ID_VENDOR=Linux_3.0.35-lab126_with_fsl-usb2-udc E: ID_VENDOR_ENC=Linux\x203.0.35-lab126\x20with\x20fsl-usb2-udc E: ID_VENDOR_ID=0525 E: ID_MODEL=RNDIS_Ethernet_Gadget E: ID_MODEL_ENC=RNDIS\x2fEthernet\x20Gadget E: ID_MODEL_ID=a4a2 E: ID_REVISION=0399 E: ID_SERIAL=Linux_3.0.35-lab126_with_fsl-usb2-udc_RNDIS_Ethernet_Gadget E: ID_TYPE=generic E: ID_BUS=usb E: ID_USB_INTERFACES=:0202ff:0a0000:020600: E: ID_USB_INTERFACE_NUM=00 E: ID_USB_DRIVER=cdc_ether E: ID_USB_CLASS_FROM_DATABASE=Communications E: ID_VENDOR_FROM_DATABASE=Netchip Technology, Inc. E: ID_MODEL_FROM_DATABASE=Linux-USB Ethernet/RNDIS Gadget E: ID_MM_CANDIDATE=1 E: ID_PATH=pci-0000:07:00.4-usb-0:2:1.0 E: ID_PATH_TAG=pci-0000_07_00_4-usb-0_2_1_0 E: ID_NET_DRIVER=cdc_ether E: ID_NET_LINK_FILE=/lib/systemd/network/99-default.link E: ID_NET_NAME=enp7s0f4u2i1
Zaujímavé položky sú napríklad ID_VENDOR
, alebo ID_NET_NAME_MAC
. Keďže sú názvy nespoľahlivé, začnem premenovaním zariadenia na niečo stabilné pomocou MAC adresy (dal by sa zneužiť aj výrobca). Vytvorím si, alebo pridám do súboru /etc/udev/rules.d/98-network.rules
nasledujúci riadok:
SUBSYSTEM=="net", ACTION=="add", ENV{ID_VENDOR}=="Linux_3.0.35-lab126_with_fsl-usb2-udc", NAME="eth_kindle"
Toto pravidlo nastaví názov sieťového zariadenia na eth_kindle
. Teraz nasleduje pomerne hnusný hack, ktorý si môžem dovoliť, pretože môj počítač používam sám.
Pod rootom teda vytvorím adresár /mnt/kindle
príkazom mkdir -p /mnt/kindle
a priradím mu práva chown uzivatel:skupina /mnt/kindle
, v mojom prípade chown mirec:mirec /mnt/kindle
.
Do systému nainštalujem balíky sshfs
a sshpass
. Ďalej nahradím pôvodný riadok v /etc/udev/rules.d/98-network.rules
novými riadkami pre pripojenie a odpojenie (pribudol len príkaz RUN
).
SUBSYSTEM=="net", ACTION=="add", ENV{ID_VENDOR}=="Linux_3.0.35-lab126_with_fsl-usb2-udc", NAME="eth_kindle" RUN+="/etc/udev/scripts/kindle_start" SUBSYSTEM=="net", ACTION=="remove", ENV{ID_VENDOR}=="Linux_3.0.35-lab126_with_fsl-usb2-udc", NAME="eth_kindle" RUN+="/etc/udev/scripts/kindle_stop"
Skript pre odpojenie /etc/udev/scripts/kindle_stop
bude veľmi jednoduchý:
#!/bin/bash
fusermount -z -u /mnt/kindle
Samozrejme skript musí mať príkaz spustiteľný (chmod +x /etc/udev/scripts/kindle_stop
). To isté bude platiť aj pre skript kindle_start
, ktorý vyzerá takto:
#!/bin/bash ip link set eth_kindle up ip addr add 192.168.15.243/24 dev eth_kindle su uzivatel -c "sshfs root@192.168.15.244:/mnt/us/ /mnt/kindle" echo "date -s \"`date +'%Y-%m-%d %H:%M:%S'`\";hwclock -w -u"|sshpass -p "" ssh -o StrictHostKeyChecking=no root@192.168.15.244
Nastavenie adresy nemusím vysvetľovať. Pripojenie sa vykonáva pod bežným užívateľom cez fuse
. Čo tam však robí date?
Jailbreaknutý Kindle používam permanentne v režime lietadla. V takom prípade nie je možné synchronizovať čas. Presnosť hodín reálneho času na mojej čítačke je nič moc, okolo 30s chyba mesačne. Nie je to veľa, ale je to dosť na to, aby sa mi nechcelo nastavovať čas. To však nevysvetľuje dôvod, prečo som nepoužil NTP.
Časová zóna /etc/localtime
je odkaz na /var/local/system/tz
, čo je nejaký súbor generovaný Amazonom. Koreňový adresár zostáva permanentne read-only. Príkaz date
vypíše toto:
# date Sat May 14 09:12:30 GMT+2:22100 2022
Čo je GMT+2:22100? Prečo je časový posun oproti GMT 8520 sekúnd (2 hodiny a 22 minút)? Nastavenie času cez ntp
mi posunie hodiny o 22 minút. Mohol by som teoreticky nahradiť súbor časovej zóny, ale pri ďalšom štarte by sa to zase rozbilo.
Správa kníh cez Calibre
Na začiatok je potrebné spustiť welcome wizard. Pre Kindle sa totiž štandardne používajú formáty AZW3
a MOBI
. Teoreticky sa Calibre tvári, že je možné ich zmeniť v nastaveniach, ale stále existujú automatické konverzie, ktoré mení len wizard. Bez tohto kroku by síce boli knihy správne odosielané v EPUB
-e, ale správy by sa napríklad stále ukladali do MOBI
.
Čítačka sa dá pridať cez tlačidlo pripojenia a výber adresára /mnt/kindle/documents/
.
V nastaveniach adresára je potrebné ešte nastaviť podporované formáty, pretože pri zvolení generického zariadenia bude akceptovať aj formáty, ktoré nepodporuje. Taktiež odporúčam zmeniť šablónu (pattern, ktorý sa používa pre uladanie), pretože štandardne ukladá v adresárovej štruktúre Autor / Kniha
, čo sa mi na čítačke nepoužíva práve najpríjemnejšie.
KOReader sync
Dodatočný zásuvný modul, KOReader sync slúži na načítanie niektorých atribútov z čítačky. Nastavenie nie je úplne najintuitívnejšie, preto skúsim vysvetliť ako funguje napríklad taká synchronizácia priebehu čítania.
Najskôr je potrebné vytvoriť si stĺpec, do ktorého sa bude údaj načítavať. V tomto prípade to bude desatinné číslo formátované (python formátovanie) ako percentuálna hodnota.
V nastaveniach KOReader sync už stačí len spárovať import so stĺpcom a zobraziť stĺpec progress v zozname kníh. Po stlačení tlačidla synchronizácie sa načíta stav kníh zo zariadenia.
Spustenie bez framweorku
KOReader môže na zariadeniach Amazon Kindle naraziť na nedostatok operačnej pamäte. Pri náročnejších PDF súboroch zvykne štandardná čítačka padať na nedostatku pamäte. Zvyčajne na chvíľu pomôže reštart. KOReader má rovnaký problém, aj keď sám veľa operačnej pamäte nepotrebuje.
Pre pochopenie nasledujúcej časti bude potrebné poznať niektoré komponenty. Začnem preto rovno malým slovníkom.
- Framework
- GUI rozhranie od amazonu (napísané v jave)
- cvm
- Hlavný proces GUI
- KAF
- Kindle amazon framework?
- awesome
- Desktopové prostredie, skriptovateľné v LUA
- lipc
- IPC protokol využívajúci D-Bus, podmnožina D-Bus, existuje open source implementácia
- powerd
- Daemon starajúci sa o napájanie
- blanket
- Proces starajúci sa o zobrazenie bootovacej obrazovky, screensavera, priebehu aktualizácie … Rôzne moduly sú implementované ako pluginy a existuje open source implementácia screensavera.
- upstart
- Init systém vytvorený spoločnosťou Canonical
- LibElektra
- Konfiguračné registry v stromovej štruktúre (niečo ako registry vo windowse)
- kdb
- Nástroj na prácu s LibElektra registrami.
Výpis htop
vyzerá ako z viac-menej normálneho linuxového desktopu. Nájdeme tu paradoxne X.org server, čo je celkom zaujímavá voľba. Jednouchšie embedded systémy používajú väčšinou priamy prístup do framebufferu. Výkonnejšie zariadenia zase zvyknú používať kompozitor s podporou OpenGL ES. Prečo je použitý X.org?
Jednou z možných príčin je absencia grafickej akcelerácie na použitom SoC (Freescale i.MX 6). Druhou je nenažranosť GPU. Nedávno som sa hral so spotrebou môjho thinkpadu a prekvapujúco najnižšiu spotrebu som dosahoval pri spustenom X.org bez aplikácií využívajúcich grafickú akceleráciu. Ak som používal napríklad akcelerovaný emulátor terminálu Alacritty namiesto rxvt-unicode, spotreba bola o nejakú tú desatinu watu vyššia. Pri zapnutom waylande to už bolo viac o 0.3 W (prostredie sway).
Spustenie z konzoly
GUI sa štartuje skriptom /etc/upstart/lab126_gui
. Zastaviť sa dá jednoducho príkazom stop lab126_gui
.
Teraz stačí prejsť do adresára /mnt/us/koreader
a spustiť lua skript reader.lua
:
cd /mnt/us/koreader
./reader.lua
Výsledok vyzerá dobre. Čítačka funguje a pamäť je poloprázdna.
Spustenie z KUAL menu
Skript pre spustenie KOReadera implementuje ukončenie frameworku. Stačí povoliť položku v KUAL menu odstránením podmienky v extensions/koreader/menu.json.
Všetko okrem tlačidla power funguje bez problémov. V nasledujúcom texte sa budem venovať len oprave obsluhy tlačidla power.
Oprava power - lamer štýl
Tento spôsob považujem za slepú uličku, ale možno niekoho zaujme.
Situácia je taká, že nereaguje na tlačidlo power. Na začiatok je dobré zistiť, či sa udalosť generuje cez štandardný linuxový input subsystém. Aby som si to overil, skúsim najväčšiu blbosť: cat /dev/input/event0
. Stlačím power a vypadne na mňa binárny humus. Tlačidlo teda mám a je pripojené na event0
.
Do kódu inicializácie zariadenia pridávam:
self.input.open("/dev/input/event0")
print(ev.code)
Po spustení ./reader.lua
a stlačení tlačidla vidím v konzole číslo 116
. Ďalej do kódu inicializácie pridávam mapovanie udalosti 116
s internou udalosťou Power
.
self.input = require("device/input"):new{ device = self, event_map = { [116] = "Power", }, }
Interne sa táto udalosť konvertuje na PowerPress a PowerRelease.
Zostáva už len zaregistrovať udalosti do kódu isKindle a filtrovať udalosti ak je spustený screensaver (keďže teraz píšeme len softvérovú podporu, zostáva dotyková vrstva stále aktívna, bez filtrovania by sa dotykom vypol screensaver):
self.event_handlers["PowerRelease"] = function() if not self._entered_poweroff_stage then UIManager:unschedule(self.poweroff_action) -- resume if we were suspended if Device.screen_saver_mode then Device:outofScreenSaver() self:_afterResume() else self:_beforeSuspend() Device:intoScreenSaver() Device:suspend() end end end self.event_handlers["__default__"] = function(input_event) if not Device.screen_saver_mode then self:sendEvent(input_event) end end
Po štarte KOReadera nabehne po stlačení tlačidlá power screensaver. Funkcionalita je viac-menej hotová, akurát sa čítačka nehibernuje. Nie je to úplne ideálne riešenie, ale na zablokovanie displaya a občasné utretie prachu postačí ;)
Oprava power - normálne riešenie
Najskôr začnem simuláciou udalostí. Utilita powerd_test
slúži na simuláciu power tlačidla.
powerd_test: Utility to test powerd Options: s : Print status i : Simulate input event p : Simulate power button pressed event h : Simulate power button held event l : Simulate low batt low event d <value> : Set defer sleep property to given value r : Simulate a random series of events at random intervals. Max interval is 10s by default. b<optional>: Set fake battery level if a value is provided. Remember to there is no space between b and value. If no value is provided, it interactively accepts values from you and sets fake battery level t <value> : Max interval between events in seconds. Valid only when used with -r NOTE : Only one of i,p,d, b and r can be used at one time
Príkaz powerd_test -p
so zapnutým frameworkom uzamkne obrazovku, ale bez frameworku nerobí nič. Keďže nie sú dostupné zdrojové kódy, pozriem sa programu na zúbok pomocou dekompilátora ghidra.
Super nástroj je Search / Strings
. Po vyhľadané výsledkov power mi udrelo do očí dbg_power_button_pressed
. Po dvojkliku na referenciu som sa dostal k funkcii na screenshote.
Utilitka podľa všetkého spúšťa príkaz lipc-send-event com.lab126.powerd.debug dbg_power_button_pressed
. Príkaz prebehne bez chyby, takže problém bude asi niekde inde. Pokračujem dekompiláciou programu powerd
.
V ňom si všímam reťazce začínajúce sa na dbg_
. Vidím tu zaujímavé reťazce dbg_mag_sensor_opened
a dbg_mag_sensor_closed
. Po spustení lipc-send-event com.lab126.powerd.debug dbg_mag_sensor_closed
sa spustí screensaver a stav zariadenia sa zmení na Screen Saver
:
powerd_test -s Powerd state: Screen Saver Remaining time in this state: Unknown defer_suspend:0 suspend_grace:0 prevent_screen_saver:0 drive_mode_state:1 PRIMARY BATTERY Battery Level: 100% Last batt event at: 100% Charging: Yes Battery logging: On
Po spustení lipc-send-event com.lab126.powerd.debug dbg_mag_sensor_opened
je zariadenie opäť v aktívnom stave:
powerd_test -s Powerd state: Active Remaining time in this state: 598.386396 …
Chyba bude niekde v spracovaní udalosti tlačidla. Podľa reťazcov nachádzam v dekompilátore nasledujúci kód:
iVar2 = LipcRegisterIntProperty(DAT_0003957c,"powerButton",0,&LAB_000145f0,0);
Existenciu property potvrdzuje aj výpis lipc-probe
:
lipc-probe com.lab126.powerd w Int touchScreenSaverTimeout w Int deferSuspend w Int wakeUp r Int flRawIntensity w Int rtcWakeup r Str state w Int suspendGrace r Str battStateInfo w Int deferRestart w Str shutdownAlertResponse r Int battTemperature w Int addSuspendLevels rw Str logLevel r Int battLevel rw Int preventScreenSaver rw Str logMask w Int sendPendingResume rw Int flAuto rw Int flIntensity r Int flMaxIntensity w Str rtcWakeup2 w Int abortSuspend r Int flTurboBrightnessIntensity w Int powerButton w Int flStartup r Str status r Int isLowTempState r Int isCharging
Volanie lipc-set-prop com.lab126.powerd powerButton 1
však nerobí nič. V zásade som neprišiel na nič zaujímavé okrem toho, že kód obsahuje množstvo ladiacich výpisov. V property však vidím logMask
. Nastavujem preto lipc-set-prop com.lab126.powerd logMask "0xffff"
a pozerám, čo sa deje v /var/log/messages
. Nastavenie property powerButton
vypíše nasledujúci kód:
220514:194019 powerd[5962]: E lipc:dbuserr:name=org.freedesktop.DBus.Error.ServiceUnknown:Receive Dbus error message (DBUS_MESSAGE_TYPE_ERROR): The name com.lab126.kaf was not provided by any .service files 220514:194019 powerd[5962]: D lipc:axstime:prop=frameworkStarted, source=com.lab126.kaf, ms=4:lipc property access time in milliseconds (PrvAccessPropertySync:access_properties.c:702) 220514:194019 powerd[5962]: I def:pbisplash::Splash screen is on. Ignoring power button
Výpis relevantnej časti kódu z dekompilátora vyzerá nasledovne:
iVar2 = FUN_000167bc(); if (iVar2 != 0) { if (DAT_000395ec != (undefined **)0x0) { FUN_0001f0bc(DAT_000395ec); DAT_00038ac4 = 0xffffffff; DAT_000395ec = (undefined **)0x0; return 0; } pcVar4 = "active_handle_power_button"; pcVar3 = "PowerButtonPress"; goto LAB_0001ce18; } if ((DAT_00039450 & 0x800000) == 0) { return 0; } iVar2 = 1; pcVar4 = "I def:pbisplash::Splash screen is on. Ignoring power button";
Ak je iVar2
0
, potom je vraj zobrazený screen saver. Zostáva preskúmať funkciu FUN_000167bc
.
Kód najskôr zisťuje hodnoty registrov system/daemon/powerd/KAF_NAME
a system/daemon/powerd/KAF_FRAMEWORK_STARTED
. Pozrime sa na tieto hodnoty pomocou nástroja kdb
:
kdb get system/daemon/powerd/KAF_NAME com.lab126.kaf kdb get system/daemon/powerd/KAF_FRAMEWORK_STARTED frameworkStarted
Ďalej sa volá lipc
. Volanie je ekvivalentné príkazu:
lipc-get-prop com.lab126.kaf frameworkStarted com.lab126.kaf failed to access property frameworkStarted (0x3 lipcErrNoSuchSource)
Funkcia považuje za zobrazený splash screen ak framework nehlási vlastnosť frameworkStarted
. Riešením je spustiť proces, ktorý sa bude odpovedať na property frameworkStarted
. Vytvorím si jednoduchý súbor test.lua
a spustím ho (lua test.lua
):
require("liblipclua") lipc_handle, error_message, error_number = lipc.init("com.lab126.kaf") local readonly_property = lipc_handle:register_int_property("frameworkStarted", "r") readonly_property.value = 1 while true do lipc.run_event_loop(math.huge) end
Po spustení sa však nič nezmenilo. Do pozornosti dávam vo výpise riadok return 0
. Podľa dekompilátora funkcia vráti vždy 0
bez ohľadu na predchádzajúce volania. Dekompilácia optimalizovaného C kódu je zložitá. Výsledný kód nie je vždy korektný a obyčajne sa dá spoznať podľa nejakej blbosti, napríklad funkcie, ktorá vracia vždy konštantnú hodnotu. Toto je presne ten prípad. V disassembleri je však korektne viditeľné pokračovanie funkcie, ktoré načítava registre system/daemon/powerd/BLANKET_NAME
a system/daemon/powerd/BLANKET_LOAD
.
kdb get system/daemon/powerd/BLANKET_NAME com.lab126.blanket kdb get system/daemon/powerd/BLANKET_LOAD load lipc-get-prop com.lab126.blanket load langpicker blankwindow usb splash
Ďalej sa testuje, či property load
služby com.lab126.blanket
obsahuje text splash
. Služba blanket
má zaujímavé property ako load
a unload
, pomocou ktorých sa dajú načítať, alebo odobrať pluginy:
lipc-probe com.lab126.blanket rw Str unload rw Str logMask rw Str load w Str uiQuery rw Str logLevel
Po odobratí splash (lipc-set-prop com.lab126.blanket unload splash
) začne fungovať uspávanie tlačidlom power.
Z tohto kódu som vytvoril plugin pre KOReader, ktorý stačí uploadnúť do adresára koreader/plugins
a reštartovať KOReader.
Teraz funguje všetko korektne vrátane obsluhy tlačidla power. Spotreba čítačky je približne rovnaká, ako so spusteným frameworkom.
Bonus tip - X11 aplikácie / Kterm
Pri hraní sa s KOReaderom som napísal ešte jeden malý plugin, ktorý umožňuje naštartovať X11 aplikácie.
Spustenie X11 aplikácie nie je až tak priamočiare, ako by sa mohlo zdať, pretože KOReader vykresľuje priamo na framebuffer. Po štarte aplikácie nesmie KOReader nič vykresľovať, ale to nie je až taký problém, pretože volanie os.execute
blokuje vykonávanie až ukončenia aplikácie.
Väčším problémom je, že KOReader blokuje vstup. Okno terminálu síce nabehne, ale nebude mať prijímať žiadne udalosti dotyku. Problém som vyriešil jednoducho odpojením vstupných vstupov pred štartom terminálu a obnovením vstupov po štarte. Kód nakoniec nie je vôbec zložitý.
Nakoniec ešte ukážkové video:
Bonus tip 2 - pripojenie ku Calibre ako "bezdrôtové zariadenie" cez USBNetwork
KOReader v štandardnej inštalácii obsahuje plugin pre spojenie s Calibre cez wifi. Po odstránení jednej podmienky a nastavení pevnej IP adresy / portu je možné zapnúť pripojenie na Calibre bez zapnutej WiFi.
Pri vypnutom KOReaderi je potrebné upraviť settings.reader.lua
a pridať, alebo upraviť položky:
["calibre_wireless_url"] = { ["address"] = "192.168.15.243", ["port"] = 9090, }, ["inbox_dir"] = "/mnt/us/documents",
Aby bolo spojenie automatické, napísal som malý plugin, ktorý bude po pripojení USB kábla bude každú sekundu kontrolovať danú IP adresu a port. Ak je port otvorený, pripojí sa automaticky na Calibre. V praxi to vyzerá takto:
Boot priamo do KOReaderu
Kindle bootuje veľmi dlho. Samotný systém do X.org serveru dokáže naštartovať za 10s, ale vstavaná aplikácia čítačky štartuje 2-3 minúty. Teoreticky by bolo možné deaktivovať službu gui cez upstart a štartovať priamo KOReader, vďaka čomu by bola čítačka použiteľná za asi 15s po reštarte. Reálne sú tu rôzne kontroly, či Kindle nabootoval a v prípade 3 neúspešných pokusov sa zasekne na obrazovke oznamujúcej nutnosť opravy. Pravdu povediac neviem, či v tomto režime nabehne vôbec SSH daemon a nechcem riskovať, že si úplne zablokujem prístup. Keby napriek tomu niekto chcel experimentovať, odporúčam pozrieť toto vlákno, kde je ukážka úpravy upstart skriptu.
Takže milé deti, neskúšajte to doma (aspoň nie, ak nemáte pripravený UART a nástroje na rozobratie).
Pre pridávanie komentárov sa musíte prihlásiť.
dakujem za fajn clanok. KOReader som pouzival na citacke, Literatu som si nahodil na tvoju radu. Je to celkom fajn.
KOReader ma jednu fajn ficurku - perception expander ktory v kombinacii s nastavenim okrajov moze priniest lepsi citatelsky zazitok.
Vynikajúci článok.
Ja používam hádam 15 rokov starý PocketBook. Batéria ešte drží, ale trochu ma lákajú tie nové (pre mňa) fičúry ako pod(nad)svietenie, RSS alebo dotykové ovládanie (predsa len, pomocou štvorsmerného tlačidla + potvrdzovanie je všetko, na dnešnú dobu, spomalené).
V tom čase ma PocketBook lákal na to, že zvládal bez konverzie veľké množstvo formátov, nemal reklamy a potrebu registrácie ako Kindle a displej mal porovnateľný s Kindle (neviem už, aký to bol model - mal klávesnicu).
Ligatúry nezvláda (ani jeden z dvoch pôvodne nainštalovaných programov), PDF-ká s reflowom ako-tak, prvé spustenie pri veľkých súboroch (napr. .docx) trvá dlhšie, zrejme kvôli preformátovaniu a pod.
Ale inak funguje dobre a preto som na vážkach, či kúpiť niečo nové a ak áno, tak čo.
Mimochodom, ten KOreader má i verziu pre GNU/linux, takže sa dá vyskúšať na bežnom PC.
Ja by som išiel do upgradu len kvôli osvetleniu. Samozrejme záleží na tom kto ako tú čítačku používa, ale ja bežne čítam pred spaním so zhasnutým svetlom, takže pre mňa rozhodne dôležitá funkcia.