RaspberryPi Zero RPi Display Direct fb (no X) Touch "Patch"

In case you use Raspberry Pi with RPi Display,Touch Interface and Direct Framebuffer, this evdev.py “patch” will permit use de Touch (XPT2046/ADS7846) in the same way that in ESP32 and Swap Axes.

import time
import sys

class mouse_touch:
    def __init__(self, scr=None, cursor=None, device='/dev/input/event0',
        format='llHHI',
        EVDEV_HOR_MIN=None, 
        EVDEV_HOR_MAX=None,
        EVDEV_VER_MIN=None, 
        EVDEV_VER_MAX=None,
        SWAP_AXES=False):

        # Open evdev and initialize members
        self.evdev = open(device, 'rb')
        self.poll = select.poll()
        self.poll.register(self.evdev.fileno())
        self.scr = scr if scr else lv.scr_act()
        self.cursor = cursor if cursor else crosshair_cursor(self.scr)
        self.hor_res = self.scr.get_width()
        self.ver_res = self.scr.get_height()
        self.format=format
        self.EVDEV_HOR_MIN = EVDEV_HOR_MIN
        self.EVDEV_HOR_MAX = EVDEV_HOR_MAX
        self.EVDEV_VER_MIN = EVDEV_VER_MIN
        self.EVDEV_VER_MAX = EVDEV_VER_MAX
        self.SWAP_AXES = SWAP_AXES

        # Register LVGL indev driver
        self.indev_drv = lv.indev_drv_t()
        self.indev_drv.init()
        self.indev_drv.type = lv.INDEV_TYPE.POINTER
        self.indev_drv.read_cb = self.mouse_read
        self.indev = self.indev_drv.register()
 

    def mapvalue(self,value_in, in_min, in_max, out_min, out_max):
        mapping = int((value_in - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        return mapping
        

    def mouse_read(self, 
        indev_drv, 
        data) -> int:

        EVENT_SIZE = ustruct.calcsize(self.format)
        EV_ABS          = 0x03
        EV_KEY          = 0x01
        ABS_X           = 0x00
        ABS_Y           = 0x01
        ABS_RX          = 0x03
        ABS_PRESSURE    = 0x18
        ABS_BRAKE       = 0x0a
        ABS_MT_POSITION_X = 0x35    
        ABS_MT_POSITION_Y = 0x36    
        ABS_MT_TRACKING_ID = 0x39    
        ABS_MAX         = 0x3f
        BTN_TOUCH       = 0x14a
        pressure        = None


        
        # Check if there is input to be read from evdev
        
        while self.poll.poll()[0][1] & select.POLLIN:

            if self.EVDEV_HOR_MIN is None:
                self.EVDEV_HOR_MIN=0
            if self.EVDEV_VER_MIN is None:
                self.EVDEV_VER_MIN=0
            if self.EVDEV_HOR_MAX is None:
                self.EVDEV_HOR_MAX = self.hor_res
            if self.EVDEV_VER_MAX is None:
                self.EVDEV_VER_MAX = self.ver_res
            
            # Read and parse evdev touch data

            (tv_sec, tv_usec, typeval, code, value) = ustruct.unpack(self.format, self.evdev.read(EVENT_SIZE))
            
            
            if typeval==EV_ABS:
                if code == ABS_X:
                    if self.SWAP_AXES is True:
                        data.point.y = self.mapvalue(value, self.EVDEV_VER_MIN, self.EVDEV_VER_MAX, 0, self.scr.get_height())
                    else:
                        data.point.x = self.mapvalue(value, self.EVDEV_HOR_MIN, self.EVDEV_HOR_MAX, 0, self.scr.get_width())
                elif code == ABS_Y:
                    if self.SWAP_AXES is True:
                        data.point.x = self.mapvalue(value, self.EVDEV_HOR_MIN, self.EVDEV_HOR_MAX, 0, self.scr.get_width())
                    else:
                        data.point.y = self.mapvalue(value, self.EVDEV_VER_MIN, self.EVDEV_VER_MAX, 0, self.scr.get_height())
                elif code == ABS_MT_POSITION_X:
                    if self.SWAP_AXES is True:
                        data.point.y = self.mapvalue(value, self.EVDEV_VER_MIN, self.EVDEV_VER_MAX, 0, self.scr.get_height())
                    else:
                        data.point.x = self.mapvalue(value, self.EVDEV_HOR_MIN, self.EVDEV_HOR_MAX, 0, self.scr.get_width())

                elif code == ABS_MT_POSITION_Y:
                    if self.SWAP_AXES is True:
                        data.point.x = self.mapvalue(value, self.EVDEV_HOR_MIN, self.EVDEV_HOR_MAX, 0, self.scr.get_width())
                    else:
                        data.point.y = self.mapvalue(value, self.EVDEV_VER_MIN, self.EVDEV_VER_MAX, 0, self.scr.get_height())

                elif code == ABS_MT_TRACKING_ID:
                    if value == -1:
                        data.state = lv.INDEV_STATE.RELEASED
                    else:
                        data.state = lv.INDEV_STATE.PRESSED
            elif typeval == EV_KEY:
                if code == BTN_TOUCH:
                    if value == 0:
                        data.state = lv.INDEV_STATE.RELEASED
                    elif value == 1:
                        data.state = lv.INDEV_STATE.PRESSED
        


        if self.cursor: self.cursor(data)
        return 0

Example

import lvgl as lv
import fb
import evdev

class pointer_cursor:
    def __init__(self, scr=None):
        self.scr = scr if scr else lv.scr_act()
        self.img = lv.img(self.scr)
        self.img.set_src(lv.SYMBOL.GPS)
        self.cursor_style = lv.style_t()
        self.cursor_img = self.img

    def __call__(self, data):
        #print("State: %d - ValorX: %d  ValorY:%d" % (data.state, data.point.x, data.point.y))
        self.cursor_img.set_pos(data.point.x-15, data.point.y)

    def delete(self):
        self.cursor_hor.delete()
        self.cursor_ver.delete()



lv.init()
fb.init()

# Register FB driver

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

# Create a screen 

#scr = lv.obj()
scr = lv.scr_act()
btn = lv.btn(scr)
btn.align(lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text("Hello World!")
cb = lv.checkbox(scr)
cb.set_text("Click Me")
cb.set_pos(10,160)

# Register Touch driver, pointer and calibration Or None if does not need it.
mouse = evdev.mouse_touch(scr=scr, cursor=pointer_cursor(scr=lv.scr_act()),
		EVDEV_HOR_MIN=3930, 
        EVDEV_HOR_MAX=236,
        EVDEV_VER_MIN=165, 
        EVDEV_VER_MAX=3929,
        SWAP_AXES=True)



# Load the screen

lv.scr_load(scr)