How to implement event loop in UNIX port?

Dear all,

after having implemented the “evdev” module in the UNIX port for Raspberry Pi, I’ve now reached the next step which is to implement a proper event loop (for example, taken care of by the lvesp32 / lvstm32 modules). As a quick-and-dirty hack I used

while True:
      lv.tick_inc(1)
      lv.task_handler()
      sleep_ms(100)

which seems to work. However, in various posts I read that you shouldn’t call them together in the same loop…
Would it be an option to use the “uasyncio” module instead? From what I’ve seen so far, it features timer and task functions…

Hmm… in SDL the Unix port handles this for you, here. I assume you are not using the SDL driver?

Maybe you could reuse the implementation of this in whatever driver you are using.

Dear embeddedt,

yes, you’re right, I’m using LVGL with the framebuffer driver and a SPI display shield…
Thanks, I’ll have a look at it!

Hi @gersch07
If I understand correctly, you are using Micropython on Linux with FrameBuffer.

Do you use the modfb.c driver?
This driver handles both the Frame-Buffer display on Linux systems and the periodic calls to tick_inc and task_handler.

If you have your own FrameBuffer driver for some reason, you can use modfb.c as a reference.
Here are the lines of code that create a thread and periodically call tick_inc and task_handler:

Hi @amirgon,

yes, that’s correct, I’m using the “modfb.c” driver. The code I’m using for testing is as follows:

import lvgl as lv
import fb
from evdev import evdev
from time import sleep_ms

lv.init()
fb.init()
touch = evdev()
touch.init()

disp_buf1 = lv.disp_buf_t()
buf1_1 = bytearray(480*10)
disp_buf1.init(buf1_1, None, len(buf1_1)//4)
disp_drv = lv.disp_drv_t()
disp_drv.init()
disp_drv.buffer = disp_buf1
disp_drv.flush_cb = fb.flush
disp_drv.hor_res = 480
disp_drv.ver_res = 320
disp_drv.register()

indev_drv = lv.indev_drv_t()
indev_drv.init()
indev_drv.type = lv.INDEV_TYPE.POINTER
indev_drv.read_cb = touch.read
indev_drv.register()

scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.scr_act(), lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text(“Hello World!”)

lv.scr_load(scr)

This produces a static “Hello World” button that doesn’t react to touch events, unless I use the hacky event loop above.
Could it be that framebuffer and SDL drivers work a bit differently? It looks like in SDL a thread (“mp_thread”, see line 84) is temporarily killed in line 41 to avoid REPL blocking the input. I think this is missing for “modfb.c”…

You are right.
The kill signal is sigusr1 which is not ment to kill, only to interrupt the REPL and give the event loop a chance to run.
I would need to fix that, but I’ll get to it only next week.
Essentially, we need two things:

  • send the sigusr1 signal periodically
  • register sigusr1 as an empty signal that does nothing (this prevents killing the application, but enough for interrupting the REPL)

You can try this yourself, or wait for about 1 week until I fix this on modfb code.

Update -

I’ve fixed the FB driver.
I’ve also added a pure-python evdev driver.

This is the “Hello World” example script that uses FB + evdev:

Hi @amirgon,

thanks a lot! I was about to try it myself today (for learning purposes I’ll still do that before testing your code :wink: ) !

Is there a plan to add touch panel driver in evdev.py?

Not that I know of.
But you are welcome to add it and send a PR!

I’ve already done this function, but there’s a little problem with performance. I use Python thread to capture touch input events, but the CPU usage is too high.