From afb70f510a24a5a117a366dc2a8f66a96767b0c2 Mon Sep 17 00:00:00 2001 From: pcouy Date: Thu, 30 Apr 2026 13:16:38 +0200 Subject: [PATCH 1/9] [rmkit] Upgrade base docker image to latest for kobo --- docker/Dockerfile.kobo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile.kobo b/docker/Dockerfile.kobo index d99fef32..a728460b 100644 --- a/docker/Dockerfile.kobo +++ b/docker/Dockerfile.kobo @@ -1,6 +1,6 @@ -FROM ghcr.io/toltec-dev/python:v1.1 +FROM ghcr.io/toltec-dev/python:v4.0 -RUN pip3 install okp +RUN pip3 install --break-system-packages okp WORKDIR /opt/ RUN apt update From 40b08d8fcf5489f78b6ebca497e9843e066fb28d Mon Sep 17 00:00:00 2001 From: pcouy Date: Tue, 5 May 2026 11:20:40 +0200 Subject: [PATCH 2/9] [rmkit] More kobo building related stuff - Add kobo/ output dir to .gitignore - Fix `make remux_docker` and other similar targets for kobo => Build is a lot faster than rebuilding everything everytime --- .gitignore | 1 + scripts/build/docker_build.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6bc851d5..bdd12034 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ fb.pnm fb.raw screen.* output +kobo/ tags tmp *.o diff --git a/scripts/build/docker_build.sh b/scripts/build/docker_build.sh index 7775699a..99537ea3 100644 --- a/scripts/build/docker_build.sh +++ b/scripts/build/docker_build.sh @@ -2,10 +2,11 @@ OUTDIR=artifacts PACKAGE="${1}" TARGET=${TARGET:-rm} FBINK=${FBINK} +CROSS_TC=${CROSS_TC:-arm-linux-gnueabihf} mkdir ${OUTDIR} docker run -i --rm -v "${PWD}/${OUTDIR}:/mnt/artifacts" rmkit:${TARGET} /bin/bash << COMMANDS mkdir -p src/build -FBINK=${FBINK} TARGET=${TARGET} make ${PACKAGE} +CROSS_TC=${CROSS_TC} FBINK=${FBINK} TARGET=${TARGET} make ${PACKAGE} mkdir -p /mnt/artifacts/${TARGET}/ cp -r src/build/* /mnt/artifacts/${TARGET}/ chown -R $(id -u):$(id -u) /mnt/artifacts From a823f4c34af4dca251f6747bf9489ec441e17dfb Mon Sep 17 00:00:00 2001 From: pcouy Date: Thu, 30 Apr 2026 13:17:02 +0200 Subject: [PATCH 3/9] [remux] Prevent more than 1 remux instance from running on kobo Updated the kobo wrapper script. Before this, I would sometimes have 2 remux instances running at the same time when testing, which caused weird issues. This is kind of a dirty hack that was quicker than finding the root cause --- scripts/build/build_kobo_root.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/build/build_kobo_root.sh b/scripts/build/build_kobo_root.sh index 151d667c..72c9b914 100644 --- a/scripts/build/build_kobo_root.sh +++ b/scripts/build/build_kobo_root.sh @@ -37,6 +37,10 @@ tar_files() { make_remux_sh() { cat > ${PLUGIN_DIR}/bin/remux.sh << REMUX_SH #!/bin/sh +while [ \$(pgrep -f "sh /opt/bin/remux.sh" | wc -l) -gt 2 ]; do + kill \$(pgrep -f "sh /opt/bin/remux.sh" | head -n1) +done +killall remux while true; do sleep 1; /opt/bin/apps/remux; From 1faf3e45d5e03f0b31b99dc5874722b18e00e396 Mon Sep 17 00:00:00 2001 From: pcouy Date: Thu, 30 Apr 2026 17:26:14 +0200 Subject: [PATCH 4/9] [ui] Fix exception storm when using remux's API Before this, when sending commands through the API, stdout would fill with exceptions with the call eventually working. After a few calls, remux would crash. Changed the locking logic in `TaskQueue`, mainly by repurposing `task_m` (which I could not find the purpose of, or anywhere in the code that acquired this lock) to prevent having more than 1 task thread running at a time. --- src/rmkit/ui/task_queue.cpy | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/rmkit/ui/task_queue.cpy b/src/rmkit/ui/task_queue.cpy index 61db7457..eb1373c2 100644 --- a/src/rmkit/ui/task_queue.cpy +++ b/src/rmkit/ui/task_queue.cpy @@ -31,33 +31,45 @@ namespace ui: TaskQueue::wakeup() static void run_tasks(): + task_q.lock() if TaskQueue::tasks.size() == 0: + task_q.unlock() return + task_q.unlock() - thread *th = nullptr - try: - th = new thread([=]() { - count := 4 - while tasks.size() > 0 and count > 0: - count-- - task_q.lock() - t := TaskQueue::tasks.front() - TaskQueue::tasks.pop_front() - task_q.unlock() - + if task_m.try_lock(): + try: + std::thread* th = new thread([=]() { task_m.lock() - t() + debug "TASK THREAD STARTED" + count := 4 + while count > 0: + count-- + task_q.lock() + if tasks.size() == 0: + task_q.unlock() + break + t := TaskQueue::tasks.front() + TaskQueue::tasks.pop_front() + task_q.unlock() + + try: + t() + catch (const std::exception& e): + debug "TASK EXEC EXC", e.what() + debug "TASKS DONE" + TaskQueue::wakeup() task_m.unlock() + }) + th->detach() + task_m.unlock() + catch (const std::exception& e): + debug "NEW THREAD EXC", e.what() TaskQueue::wakeup() - }) - th->detach() - if th != nullptr - delete th - catch (const std::exception& e): - debug "NEW THREAD EXC", e.what() - TaskQueue::wakeup() - + else: + debug "TASK QUEUE WAS RUNNING, NOT RELAUNCHING" + // class: ui::TaskQueue // The task queue is a way of scheduling tasks from side threads to be run From cdefa9ae3c67680d4df3a3792fa686d8ac330686 Mon Sep 17 00:00:00 2001 From: pcouy Date: Thu, 30 Apr 2026 17:27:21 +0200 Subject: [PATCH 5/9] [rmkit] For FBInk, add option for flashing on redraw Updated method signatures for all backends, only implemented it for FBInk --- src/rmkit/fb/fb.cpy | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/rmkit/fb/fb.cpy b/src/rmkit/fb/fb.cpy index 2599ed5b..2b6103b4 100644 --- a/src/rmkit/fb/fb.cpy +++ b/src/rmkit/fb/fb.cpy @@ -135,7 +135,7 @@ namespace framebuffer: // function: redraw_screen // if the framebuffer is dirty, redraws the dirty area // of the framebuffer. - int redraw_screen(bool full_screen=false): + int redraw_screen(bool full_screen=false, bool flash=false): if dirty == 0: return 0 @@ -145,7 +145,10 @@ namespace framebuffer: if dirty_area.y1 == 0 || dirty_area.x1 == 0: return 0 - return self.perform_redraw(full_screen) + return self.perform_redraw(full_screen, flash) + + virtual int perform_redraw(bool, bool): + return 0 virtual int perform_redraw(bool): return 0 @@ -704,7 +707,7 @@ namespace framebuffer: return vinfo.xres, vinfo.yres - int perform_redraw(bool full_screen=false): + int perform_redraw(bool full_screen=false, bool flash=false): um := 0 mxcfb_update_data update_data mxcfb_rect update_rect @@ -781,7 +784,7 @@ namespace framebuffer: virtual tuple get_virtual_size(): return self.width, self.height - int perform_redraw(bool): + int perform_redraw(bool, bool): #ifndef PERF_BUILD msync(self.fbmem, self.byte_size, MS_SYNC) self.save_png() @@ -920,8 +923,9 @@ namespace framebuffer: mem := (remarkable_color*) fbink_get_fb_pointer(self.fd, &size) return mem - int perform_redraw(bool full_screen=false): + int perform_redraw(bool full_screen=false, bool flash=false): config_.wfm_mode = self.waveform_mode + config_.is_flashing = flash if !full_screen: fbink_refresh(self.fd, dirty_area.y0, @@ -931,6 +935,7 @@ namespace framebuffer: &config_) else: fbink_refresh(self.fd, 0, 0, self.display_width, self.height, &config_) + config_.is_flashing = false return 0 void wait_for_redraw(uint32_t update_marker): @@ -979,7 +984,7 @@ namespace framebuffer: return vinfo.xres, vinfo.yres - int perform_redraw(bool full_screen=false): + int perform_redraw(bool full_screen=false, bool flash=false): um := 0 hwtcon_update_data update_data hwtcon_rect update_rect From b534723ac5f3dc10b5a2ef5271495312bcaad055 Mon Sep 17 00:00:00 2001 From: pcouy Date: Tue, 5 May 2026 12:20:04 +0200 Subject: [PATCH 6/9] [remux] Flash the screen on show/hide (FBInk only) --- src/remux/launcher.cpy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/remux/launcher.cpy b/src/remux/launcher.cpy index f6c52eec..b83019fd 100644 --- a/src/remux/launcher.cpy +++ b/src/remux/launcher.cpy @@ -31,6 +31,7 @@ #define DYNAMIC_BPP #define HAS_ROTATION #define PORTRAIT_ONLY +#define DRAW_APP_BEHIND_MODAL #define USE_GRAYSCALE_32BIT #endif @@ -178,7 +179,7 @@ class AppBackground: public ui::Widget: fb->set_rotation(vfb->rotation) #endif - fb->perform_redraw(true) + fb->perform_redraw(true, true) fb->dirty = 1 class AppDialog: public ui::Pager: From 2fd7055f42e9635173b36a693fce129fb80b0ecf Mon Sep 17 00:00:00 2001 From: pcouy Date: Tue, 5 May 2026 11:25:29 +0200 Subject: [PATCH 7/9] [rmkit] On kobo, prevent nickel from grabbing inputs Done by grabbing the inputs on first launch and releasing them only after the nickel process is running. Context : I added a modified version of koreader.sh to my remux, which does not kill nickel before launching koreader. The goal is to seamlessly switch between nickel and koreader, and to use bluetooth on y Elipsa 2E without fearing a wipe on my /mnt/onboard if I accidentaly restart nickel instead of rebooting the e-reader. This introduced an issue where koreader would not handle rotations or the power button because nickel grabs these devices and never releases them. WARNING : I have only tested this on my Kobo Elipsa 2E (Condor), this may break on other devices. Not sure why input grabbing was disabled by the preprocessor for Kobo target, but disabling it did not break anything on Condor. I also needed to add the `gpio` device to inputs, which emits orientation change events on my Condor. The `EV_MSC` + `MSC_RAW` filter might match other input devices on other e-readers. I think this would be harmless anyway, but is worth checking before merging --- src/rmkit/input/device_id.cpy | 5 ++++- src/rmkit/input/input.cpy | 33 ++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/rmkit/input/device_id.cpy b/src/rmkit/input/device_id.cpy index b6077b97..a3c84309 100644 --- a/src/rmkit/input/device_id.cpy +++ b/src/rmkit/input/device_id.cpy @@ -27,7 +27,7 @@ // event2: gpio keys // ev_key: home, left, right, power, wakeup namespace input: - enum EV_TYPE { TOUCH, STYLUS, BUTTONS, UNKNOWN, INVALID } + enum EV_TYPE { TOUCH, STYLUS, BUTTONS, GPIO, UNKNOWN, INVALID } static bool check_bit_set(int fd, int type, int i): unsigned long bit[NBITS(KEY_MAX)] ioctl(fd, EVIOCGBIT(type, KEY_MAX), bit); @@ -55,6 +55,9 @@ namespace input: if check_bit_set(fd, EV_KEY, BTN_TOOL_PEN): return TOUCH + if check_bit_set(fd, EV_MSC, MSC_RAW): + return GPIO + return UNKNOWN static bool supports_stylus(int fd): diff --git a/src/rmkit/input/input.cpy b/src/rmkit/input/input.cpy index 293f28c4..fa4342b8 100644 --- a/src/rmkit/input/input.cpy +++ b/src/rmkit/input/input.cpy @@ -8,6 +8,7 @@ #include "../defines.h" #include "../fb/fb_info.h" +#include "../shared/proc.h" #include "events.h" #include "gestures.h" #include "device_id.h" @@ -100,6 +101,7 @@ namespace input: class Input: private: + ui::TimerPtr ungrab_interval = nullptr; public: int max_fd @@ -109,6 +111,7 @@ namespace input: InputClass wacom InputClass touch InputClass button + InputClass gpio vector all_motion_events vector all_key_events @@ -133,6 +136,20 @@ namespace input: self.open_device("/dev/input/event2") self.open_device("/dev/input/event3") self.open_device("/dev/input/event4") + self.grab() + std::function ungrab_task = [=]() { + debug "UNGRAB_TASK" + vector bins = { "nickel" } + procs := proc::list_procs(bins) + if procs.size() > 0: + debug "UNGRABBING" + self.ungrab() + debug "UNGRABBED" + ui::cancel_timer(ungrab_interval) + else: + debug "RESCHEDULING" + } + ungrab_interval = ui::set_interval(ungrab_task, 500) #else if USE_RESIM: debug "MONITORING RESIM" @@ -155,7 +172,7 @@ namespace input: return void close_devices(): - vector fds = { self.touch, self.wacom, self.button} + vector fds = { self.touch, self.wacom, self.button, self.gpio} for auto in : fds: if in.fd > 0: close(in.fd) @@ -183,6 +200,10 @@ namespace input: self.touch.set_fd(fd) debug "AS TOUCH" break + case GPIO: + debug ": GPIO" + self.gpio.set_fd(fd) + break case INVALID: case UNKNOWN: default: @@ -252,17 +273,11 @@ namespace input: return void grab(): - #ifndef REMARKABLE - return - #endif - for auto fd : { self.touch.fd, self.wacom.fd, self.button.fd }: + for auto fd : { self.touch.fd, self.wacom.fd, self.button.fd, self.gpio.fd }: ioctl(fd, EVIOCGRAB, true) void ungrab(): - #ifndef REMARKABLE - return - #endif - for auto fd : { self.touch.fd, self.wacom.fd, self.button.fd }: + for auto fd : { self.touch.fd, self.wacom.fd, self.button.fd, self.gpio.fd }: ioctl(fd, EVIOCGRAB, false) void check_reopen(): From e147438426e2b8998c04eb87a4ec7b3110f89d96 Mon Sep 17 00:00:00 2001 From: pcouy Date: Tue, 5 May 2026 12:37:47 +0200 Subject: [PATCH 8/9] [remux] Grab/Ungrab inputs on kobo --- src/remux/launcher.cpy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remux/launcher.cpy b/src/remux/launcher.cpy index b83019fd..797c55be 100644 --- a/src/remux/launcher.cpy +++ b/src/remux/launcher.cpy @@ -32,6 +32,7 @@ #define HAS_ROTATION #define PORTRAIT_ONLY #define DRAW_APP_BEHIND_MODAL +#define GRAB_INPUT #define USE_GRAYSCALE_32BIT #endif @@ -348,8 +349,7 @@ class App: public IApp: ui::set_timeout([=]() { ui::MainLoop::in.unmonitor(ui::MainLoop::in.wacom.fd) }, 10) - if USE_KOREADER_WORKAROUND and CURRENT_APP != "KOReader": - ui::MainLoop::in.ungrab() + ui::MainLoop::in.ungrab() ; app_bg = new AppBackground(0, 0, w, h) From 3ea1723109e1f982f1403e32e149b0248b765967 Mon Sep 17 00:00:00 2001 From: pcouy Date: Wed, 6 May 2026 16:44:32 +0200 Subject: [PATCH 9/9] [rmkit] Fix missing header related to OKP single-header After commit 2fd7055 most apps would not build for the kobo target. Example build output below : ``` $ TARGET=kobo DOCKERFILE=Dockerfile.kobo FBINK=1 make harmony_docker CXX=arm-kobo-linux-gnueabihf-g++ okp -sh -ig "RMKIT_IMPLEMENTATION" -ns -ni -for -d ../.rmkit.h_cpp/ -o ../build/rmkit.h rmkit.cpy ../build/stb.arm.o -- -D"KOBO=1" -D"RMKIT_IMPLEMENTATION" -ldl -pthread -lpthread -fdata-sections -ffunction-sections -Wl,--gc-sections -L/rmkit/src/vendor/FBInk/Release -l:libfbink.a -D"RMKIT_FBINK=1" -O2 -static -static-libstdc++ -static-libgcc compile flags: -DKOBO=1 -DRMKIT_IMPLEMENTATION -ldl -pthread -lpthread -fdata-sections -ffunction-sections -Wl,--gc-sections -L/rmkit/src/vendor/FBInk/Release -l:libfbink.a -DRMKIT_FBINK=1 -O2 -static -static-libstdc++ -static-libgcc missing file: ../shared/proc.h missing file: ../shared/string.h generating single header /rmkit/src/rmkit/../build/rmkit.h MISSING FILE! /rmkit/src/.rmkit.h_cpp/shared/proc.h MISSING FILE! /rmkit/src/.rmkit.h_cpp/shared/string.h compiled into /rmkit/src/.rmkit.h_cpp missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/proc.h missing file: ../shared/string.h make[2]: Leaving directory '/rmkit/src/rmkit' make[1]: Leaving directory '/rmkit/src/rmkit' cd src/harmony && make make[1]: Entering directory '/rmkit/src/harmony' make compile_kobo make[2]: Entering directory '/rmkit/src/harmony' make[3]: Entering directory '/rmkit/src/vendor/FBInk' make[3]: Nothing to be done for 'staticlib'. make[3]: Leaving directory '/rmkit/src/vendor/FBInk' CXX=arm-kobo-linux-gnueabihf-g++ okp -ig "RMKIT_IMPLEMENTATION" -ns -ni -for -d ../.harmony_cpp/ -o ../build/harmony main.cpy ../build/stb.arm.o -- -D"KOBO=1" -D"RMKIT_IMPLEMENTATION" -ldl -pthread -lpthread -fdata-sections -ffunction-sections -Wl,--gc-sections -L/rmkit/src/vendor/FBInk/Release -l:libfbink.a -D"RMKIT_FBINK=1" -Wno-psabi -O2 -static -static-libstdc++ -static-libgcc compile flags: -DKOBO=1 -DRMKIT_IMPLEMENTATION -ldl -pthread -lpthread -fdata-sections -ffunction-sections -Wl,--gc-sections -L/rmkit/src/vendor/FBInk/Release -l:libfbink.a -DRMKIT_FBINK=1 -Wno-psabi -O2 -static -static-libstdc++ -static-libgcc arm-kobo-linux-gnueabihf-g++ -c /rmkit/src/.harmony_cpp/main.cpp -o /rmkit/src/.harmony_cpp/main.o -DKOBO=1 -DRMKIT_IMPLEMENTATION -ldl -pthread -lpthread -fdata-sections -ffunction-sections -Wl,--gc-sections -L/rmkit/src/vendor/FBInk/Release -l:libfbink.a -DRMKIT_FBINK=1 -Wno-psabi -O2 -static -static-libstdc++ -static-libgcc In file included from /rmkit/src/.harmony_cpp/main.cpp:3: /rmkit/src/.harmony_cpp/../build/rmkit.h: In lambda function: /rmkit/src/.harmony_cpp/../build/rmkit.h:22492:22: error: 'proc' has not been declared 22492 | auto procs = proc::list_procs(bins); | ^~~~ missing file: stb_image.h missing file: stb_image_resize.h Couldn't compile /rmkit/src/.harmony_cpp/main.cpp aborting compiled into /rmkit/src/.harmony_cpp make[2]: *** [../actions.make:42: compile_kobo] Error 1 make[2]: Leaving directory '/rmkit/src/harmony' make[1]: Leaving directory '/rmkit/src/harmony' make[1]: *** [../actions.make:11: compile] Error 2 make: *** [Makefile:18: harmony] Error 2 ``` --- src/rmkit/rmkit.cpy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rmkit/rmkit.cpy b/src/rmkit/rmkit.cpy index 529bdcb5..89e6334a 100644 --- a/src/rmkit/rmkit.cpy +++ b/src/rmkit/rmkit.cpy @@ -1,4 +1,7 @@ #include "fb/fb.h" +#ifdef KOBO +#include "../shared/proc.h" // Needed to prevent nickel from grabbing inputs +#endif #include "input/input.h" #include "util/signals.h" #include "ui/ui.h"