Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "external/kobalt"]
path = external/kobalt
url = https://github.com/42lizard/kobalt.git
8 changes: 4 additions & 4 deletions Dockerfile.qt6
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ FROM ghcr.io/fstanis/pocketbook-sdk-qt6-builder@sha256:1878e90fd4c9b8e736cd49b61
# Native build tools/QML runtime support offscreen host tests; armel libraries
# build the device app. OpenSSL 3 replaces the legacy SDK crypto archive.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl python3 pkg-config ninja-build \
curl python3 pkg-config ninja-build patch qemu-user \
libssl-dev:armel libcurl4-openssl-dev:armel libxml2-dev:armel \
libsqlite3-dev:armel libjson-c-dev:armel \
qt6-base-dev qt6-declarative-dev \
libssl-dev libcurl4-openssl-dev libxml2-dev libsqlite3-dev libjson-c-dev \
libsqlite3-dev:armel libjson-c-dev:armel libpng-dev:armel \
qt6-base-dev qt6-declarative-dev qt6-sensors-dev libqt6sql6-sqlite \
libssl-dev libcurl4-openssl-dev libxml2-dev libsqlite3-dev libjson-c-dev libpng-dev \
qml6-module-qtquick qml6-module-qtquick-window qml6-module-qtqml-workerscript \
&& rm -rf /var/lib/apt/lists/*

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ Other devices, including Verse Pro, need separate validation.

## Build

The experimental [Kobalt reader port](apps/kobalt/README.md) targets **InkPad
Color 3 (PB743K3), U743k3.6.10.2854**. It reuses Kobalt's EPUB renderer and
reading interface through the `external/kobalt` fork. It has separate build
and packaging commands; the normal app builds below remain unchanged.
Host reading tests and firmware import checks pass; device acceptance is pending.

With Docker and Compose running, from the repository root:

```sh
Expand Down
44 changes: 44 additions & 0 deletions apps/kobalt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Kobalt on PocketBook

The experimental PocketBook InkPad Color 3 port is maintained in
[42lizard/kobalt](https://github.com/42lizard/kobalt/tree/pocketbook-port), included
here as the `external/kobalt` submodule. Its buildchain is now self-contained:
the fork includes the SDK recipe, firmware preparation, ABI/runtime checks,
packaging, registration helper and tests. A standalone clone does not require
this monorepo.

## Build and install

Follow [PocketBook build and installation](../../external/kobalt/pocketbook/README.md)
for the complete workflow. From this repository:

If this checkout has old monorepo build output, move
`external/kobalt/build/pocketbook` aside once; the new container uses a different
source mount, which invalidates the old CMake caches.

```sh
git submodule update --init external/kobalt
cd external/kobalt
./build-pocketbook.sh --firmware /absolute/path/SWUPDATE.BIN --jobs 4 --test
```

The firmware input must be the official PB743K3 U743k3.6.10.2854
`SWUPDATE.BIN`; its checksum is verified. The outputs are
`external/kobalt/build/pocketbook/Kobalt-PB743K3.zip` and its `pocketbook.json`
provenance record. Firmware libraries and user data are never packaged.
Installation and registration are documented in the fork; the build does not
write to a connected device.

## Fork maintenance

Keep the fork's `main` aligned with upstream. Rebase `pocketbook-port` onto new
upstream revisions instead of merging them, preserving a linear patch series
for a future upstream contribution. Before rewriting published history, archive
device-confirmed and tested source revisions, use an explicit force-with-lease,
and update this repository's submodule pin.

The historical runtime evidence and device acceptance status are recorded in
[the firmware report](../../docs/research/kobalt-color3-runtime.md). Startup and
EPUB opening were confirmed with `6de0711`; the rebased upstream synchronization
`1a17e2f` has been installed but still awaits physical acceptance. Remaining
native integration limitations are listed in the fork's documentation.
355 changes: 355 additions & 0 deletions docs/research/kobalt-color3-runtime.md

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions docs/research/kobalt-pocketbook-port.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Kobalt on PocketBook: source feasibility

Date: 2026-10-13. Reviewed source:
[`cbc0f4e9118c0e791056e36bc0b66d90e2af027c`](https://github.com/42lizard/kobalt/tree/cbc0f4e9118c0e791056e36bc0b66d90e2af027c).
This is source research, not evidence of a working PocketBook port. Build and
device results must be recorded separately. The initial target is the current
Qt 6 InkPad 4 environment; older PocketBook compatibility is not implied.

## Assessment

A standalone PocketBook port looks feasible without replacing the reader UI or
CREngine. The strongest evidence is the existing desktop host: it already uses
the shared Qt Widgets reader and Qt-only Android controller without Nickel or
the Android runtime. Shared controls support both Kobo Qt generations. This is
an engineering inference from the existing
[source boundaries](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/docs/development.md#shared-reader-controls)
and [desktop target](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/desktop/CMakeLists.txt).
Linux alone does not establish binary, firmware or display compatibility.

The Kobo installation package is the wrong starting point. Its services depend
on Nickel keyboard, light, battery, lifecycle and presentation adapters. Its
refresh implementation uses Kobo Qt attributes, native symbol lookup and
backing-store flush hooks. Those mechanisms need a PocketBook counterpart.
[Kobo host](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/nickel/common/reader_host_services.cpp),
[Kobo display](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/nickel/qt5/refresh.cpp).

## Reuse and adaptation boundaries

- **Reader and engine:** preserve publication loading, rendering, shared Widgets
and reading state. The standalone controller already opens plain EPUB archives
and constructs the shared reader window.
[Controller](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/android/reader.cpp).
- **Host services:** implement the existing callbacks for clock, battery, page
buttons, fonts, suspend state, frontlight, dictionary, keyboard, presentation
and browser. Unsupported services still need valid callbacks/factories;
`complete()` checks their presence. Desktop provides examples.
[Interface](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/api/reader_host_services.h),
[desktop services](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/desktop/services.cpp).
- **Display:** supply `textRefresh`, `pageRefresh`, and
`setReaderRefreshWindow`. Desktop's implementations are no-ops; host rendering
therefore cannot establish actual e-ink refresh quality. Device testing must
check page turns, menu cleanup and repaint completion.
[Refresh API](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/api/kobo_refresh.h),
[desktop adapter](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/desktop/display.cpp).
- **Input:** the existing canvas converts touch to mouse events for embedded
Widgets; host services map page keys. Verify PocketBook's delivered events
before choosing a direct full-screen QWidget or reusing the canvas, avoiding
duplicate translation. The canvas uses Qt 6 touch APIs and is not a drop-in
Qt 5 host.
[Canvas](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/android/canvas.h).
- **Rotation:** the standalone controller passes a null sensor and host rotation
callback. Automatic rotation is optional at runtime, but the shared
`ReaderOrientation` still includes/calls Qt Sensors and inherits
`QOrientationFilter`. Simply omitting the Sensors link is insufficient. If
the SDK lacks it, make sensor support optional in this narrow module.
[Header](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/api/reader_orientation.h),
[implementation](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/api/reader_orientation.cpp).
- **Local progress:** keep independent Kobalt state initially. `ReadingBook`
supports a null native bridge, saves local checkpoints, and skips identical
checkpoint writes. Hosts choose directories. Use a dedicated document cache:
CREngine deletes unrelated files found in that directory.
[Book state](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/reading/reading_book.cpp),
[storage paths](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/storage/reader_storage.h).
- **Native progress and Readest:** `ReadingBridge` models a platform database
and activity recorder, while `PositionCodec` maps engine/platform positions.
These are extension points, not proof of compatible position formats. A
local-only port will not automatically participate in Readest Sync.
[Bridge](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/reading/reading_bridge.h),
[codec](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/api/position_codec.h).
- **Launch and assets:** adapt desktop bundle paths to PocketBook directories.
Desktop initializes bundled fonts/assets and accepts a book path as its first
argument. Reuse these responsibilities; a new library UI is unnecessary for
the first experiment.
[Desktop entry point](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/src/platform/desktop/main.cpp).

## Proposed minimal stages

1. Pin the user's fork as a submodule. Keep Kobalt platform changes in that fork
and PocketBook build/package integration in this repository. Preserve shared
renderer/UI sources, dependency pins, patches and notices.
2. Cross-build a standalone target around the existing controller and a small
PocketBook host. Start with one explicit DRM-free EPUB path, independent
state/cache directories, no native database bridge and no page animations.
3. Validate shared reader behavior on the host, then on InkPad 4: visible page,
touch/page keys, menus, checkpoint/reopen, Home/exit, suspend/wake and USB
lifecycle. Hardware must establish refresh quality, memory use and power
behavior.
4. Add device services incrementally. Library/default-reader registration,
native positions and Readest sync are later capabilities with separate tests.

These are proposed stages, not completed implementation. Upstream also
explicitly distinguishes portable tests from firmware integration checks in its
[test documentation](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/docs/development.md#tests).

## Initial local evidence

The fork is pinned, unmodified, under `external/kobalt` on the separate
`experiment/kobalt-pocketbook` branch. The reproducible
[compatibility probe](../../tools/kobalt-probe/README.md) links Kobalt's publication
component, Qt Widgets and InkView with the existing PocketBook toolchain.

On 2026-10-13 the ARM build and existing ABI check passed: ARM softfp,
`/lib/ld-linux.so.3`, no runtime search path, and the existing firmware
symbol-version ceilings. This establishes compilation/linkage of that component,
not runtime compatibility of the full reader. No binary was installed on a device.

The host CTest also passed: an EPUB from Kobalt's fixture generator was inspected,
compressed chapter text read, invalid ZIP input rejected and a Qt Widgets window
painted. CREngine, Kobalt's reader controls and hardware behavior are outside
this probe's coverage.

The current SDK contains Qt Widgets and Network 6.8.2, but no Qt Sensors module.
The retained `build/readest-sync/firmware-6.11.1683/` file inventories list
`libQt6Widgets.so.6.10.3` and `libQt6Network.so.6.10.3`. These local observations
support using the native Qt runtime for this target, not all PocketBook models.
The existing SDK deliberately declares imported Qt targets instead of using
Debian's normal Qt package configuration, which requires OpenGL. Kobalt's
standalone CMake wiring therefore also needs an adaptation; changing the target
name or the compiler alone is insufficient.

## Distribution inventory

Upstream labels its own source MIT, CREngine patches GPL v2 or later, and Qt
patches under Qt's licenses. It describes the combined package as AGPL v3 and
supplies corresponding source/dependency sources with releases. Preserve this
inventory instead of treating the whole executable as MIT-only. This records
upstream's stated distribution model, not a separate legal assessment.
[License statement](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/README.md#license),
[credits](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/ACKNOWLEDGEMENTS.md),
[source-bundle procedure](https://github.com/42lizard/kobalt/blob/cbc0f4e9118c0e791056e36bc0b66d90e2af027c/docs/development.md#ci-and-tagged-releases).
1 change: 1 addition & 0 deletions external/kobalt
Submodule kobalt added at c1e884
8 changes: 8 additions & 0 deletions licenses/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ not copies of the firmware controls.

## Notice provenance and scope

The optional `external/kobalt` submodule retains Nico Verbruggen's
[Kobalt license](../external/kobalt/LICENSE),
[acknowledgements](../external/kobalt/ACKNOWLEDGEMENTS.md) and dependency notices.
The compatibility probe compiles its publication component and links Qt Widgets;
it does not bundle CREngine. A complete reader port must preserve the separate
license/source inventory described in Kobalt's README; the root MIT license does
not relicense Kobalt's combined reader package.

The `*-copyright.txt` files are unmodified Debian package notices retrieved from
the project's existing Qt build image on 2026-09-14. Standard license texts are
copied from that image's `/usr/share/common-licenses`; references to that directory
Expand Down
31 changes: 31 additions & 0 deletions tools/kobalt-probe/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.21)
project(kobalt-probe LANGUAGES CXX)
option(KOBALT_PROBE_HOST "Run the compatibility probe on the build host" OFF)
set(KOBALT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../external/kobalt")
if(NOT EXISTS "${KOBALT_ROOT}/cmake/ReaderComponents.cmake")
message(FATAL_ERROR "Run git submodule update --init external/kobalt first")
endif()
set(KOBALT_QT_MAJOR 6)
if(KOBALT_PROBE_HOST)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
add_executable(kobalt-probe main.cpp)
target_compile_definitions(kobalt-probe PRIVATE KOBALT_PROBE_HOST)
else()
include(../../mk/pocketbook-qt6.cmake)
pocketbook_add_app(kobalt-probe SOURCES main.cpp)
endif()
include("${KOBALT_ROOT}/cmake/Strings.cmake")
include("${KOBALT_ROOT}/cmake/ReaderComponents.cmake")
kobalt_publication_library(kobalt-publication)
target_compile_options(kobalt-publication PRIVATE "-iquote${KOBALT_ROOT}/src")
target_include_directories(kobalt-probe PRIVATE "${KOBALT_ROOT}/src")
target_link_libraries(kobalt-probe PRIVATE kobalt-publication Qt6::Widgets Qt6::Gui Qt6::Core)
if(KOBALT_PROBE_HOST)
enable_testing()
execute_process(COMMAND "${Python3_EXECUTABLE}" -c
"import runpy,sys; runpy.run_path(sys.argv[1])['book'](sys.argv[2], chapter_count=1, paragraphs=1)"
"${KOBALT_ROOT}/tests/fixtures/reader_fixture.py" "${CMAKE_CURRENT_BINARY_DIR}/probe.epub"
COMMAND_ERROR_IS_FATAL ANY)
add_test(NAME kobalt-publication-widgets COMMAND kobalt-probe --self-test "${CMAKE_CURRENT_BINARY_DIR}/probe.epub")
set_tests_properties(kobalt-publication-widgets PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
endif()
56 changes: 56 additions & 0 deletions tools/kobalt-probe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Kobalt PocketBook compatibility probe

This is an isolated port experiment, **not a working Kobalt reader**. It compiles
the fork's unchanged publication/archive component with Qt Widgets and the
existing PocketBook startup adapter. It does not include CREngine, Kobalt's
reader UI, native reading-state integration or a device installer.

The source is pinned by the `external/kobalt` submodule to the user's
[fork](https://github.com/42lizard/kobalt). Original project:
[Nico Verbruggen's Kobalt](https://github.com/nicoverbruggen/kobalt).
Do not run Kobalt's Kobo installer on PocketBook.

## Reproduce the checks

From the repository root:

```sh
git submodule update --init external/kobalt

# Cross-compile for the current InkPad 4 target and check the ARM ABI.
docker compose run --rm qt6 sh -ec '
cmake -S tools/kobalt-probe -B build/kobalt/probe-arm -DCMAKE_BUILD_TYPE=Release
cmake --build build/kobalt/probe-arm -j2
python3 tools/check_qt6_app.py build/kobalt/probe-arm/kobalt-probe.app
'

# Run the same publication component and a Widgets paint check on the host.
docker compose run --rm qt6 sh -ec '
unset CMAKE_TOOLCHAIN_FILE
cmake -S tools/kobalt-probe -B build/kobalt/probe-host -DKOBALT_PROBE_HOST=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build/kobalt/probe-host -j2
ctest --test-dir build/kobalt/probe-host --output-on-failure
'
```

The host test generates an EPUB with Kobalt's own fixture generator, inspects
its archive, reads compressed chapter text, rejects invalid ZIP input and grabs
a painted Widgets window. It does not test EPUB layout or actual e-ink output.

The ARM binary is `build/kobalt/probe-arm/kobalt-probe.app`. If launched manually,
it shows a clearly labelled probe screen and Close button. An optional first
argument supplies a read-only EPUB archive to inspect. No native databases or
book files are written. The probe is excluded from normal app builds/releases.

## Next port milestone

Reuse the shared reader/CREngine and standalone controller through a small
PocketBook host. Build the pinned engine dependencies with the PocketBook
toolchain, make Qt Sensors optional, adapt startup/assets/state paths and connect
the existing display/host-service interfaces. Start with one explicit EPUB path
and local Kobalt progress, then validate page turns, touch, exit and suspend on
the device. Native library registration and Readest integration are separate work.

See the [source research and evidence](../../docs/research/kobalt-pocketbook-port.md).
All upstream notices remain in the submodule; a complete reader package has
additional dependency/source-distribution requirements described there.
Loading