├── .gitmodules ├── examples ├── hardware.py └── hello_world.py ├── src ├── wrapper.h ├── module.c ├── spi.h ├── display.h ├── spi.c ├── wrapper.c └── display.c ├── micropython.cmake ├── LICENSE ├── binding ├── include │ └── lv_mp_mem_custom_include.h ├── binding.cmake ├── lv_conf.h └── gen │ └── gen_mpy.py └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "binding/lvgl"] 2 | path = binding/lvgl 3 | url = https://github.com/lvgl/lvgl.git 4 | [submodule "binding/pycparser"] 5 | path = binding/pycparser 6 | url = https://github.com/eliben/pycparser.git 7 | -------------------------------------------------------------------------------- /examples/hardware.py: -------------------------------------------------------------------------------- 1 | import lvgl_esp32 2 | 3 | # Adapt these values for your own configuration 4 | spi = lvgl_esp32.SPI( 5 | 2, 6 | baudrate=80_000_000, 7 | sck=7, 8 | mosi=6, 9 | miso=8, 10 | ) 11 | spi.init() 12 | 13 | display = lvgl_esp32.Display( 14 | spi=spi, 15 | width=296, 16 | height=240, 17 | swap_xy=True, 18 | mirror_x=False, 19 | mirror_y=False, 20 | invert=False, 21 | bgr=True, 22 | reset=48, 23 | dc=4, 24 | cs=5, 25 | pixel_clock=20_000_000, 26 | ) 27 | display.init() 28 | -------------------------------------------------------------------------------- /src/wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __LVGL_ESP32_LVGL_INIT__ 2 | #define __LVGL_ESP32_LVGL_INIT__ 3 | 4 | #include "display.h" 5 | 6 | #include "lvgl.h" 7 | #include "py/obj.h" 8 | 9 | typedef struct lvgl_esp32_Wrapper_obj_t 10 | { 11 | mp_obj_base_t base; 12 | lvgl_esp32_Display_obj_t *display; 13 | 14 | size_t buf_size; 15 | uint16_t *buf1; 16 | uint16_t *buf2; 17 | 18 | lv_display_t *lv_display; 19 | } lvgl_esp32_Wrapper_obj_t; 20 | 21 | extern const mp_obj_type_t lvgl_esp32_Wrapper_type; 22 | 23 | #endif /* __LVGL_ESP32_LVGL_INIT__ */ 24 | -------------------------------------------------------------------------------- /src/module.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | #include "wrapper.h" 3 | #include "spi.h" 4 | 5 | static const mp_rom_map_elem_t lvgl_esp32_globals_table[] = { 6 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lvgl_esp32) }, 7 | { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&lvgl_esp32_SPI_type) }, 8 | { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&lvgl_esp32_Display_type) }, 9 | { MP_ROM_QSTR(MP_QSTR_Wrapper), MP_ROM_PTR(&lvgl_esp32_Wrapper_type) }, 10 | }; 11 | static MP_DEFINE_CONST_DICT(lvgl_esp32_globals, lvgl_esp32_globals_table); 12 | 13 | const mp_obj_module_t lvgl_esp32_module = { 14 | .base = { &mp_type_module }, 15 | .globals = (mp_obj_dict_t * ) & lvgl_esp32_globals, 16 | }; 17 | 18 | MP_REGISTER_MODULE(MP_QSTR_lvgl_esp32, lvgl_esp32_module); 19 | -------------------------------------------------------------------------------- /micropython.cmake: -------------------------------------------------------------------------------- 1 | # Make sure LVGL gets built 2 | include(${CMAKE_CURRENT_LIST_DIR}/binding/binding.cmake) 3 | 4 | add_library(usermod_lvgl_esp32 INTERFACE) 5 | 6 | target_sources(usermod_lvgl_esp32 INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/src/spi.c 8 | ${CMAKE_CURRENT_LIST_DIR}/src/display.c 9 | ${CMAKE_CURRENT_LIST_DIR}/src/wrapper.c 10 | ${CMAKE_CURRENT_LIST_DIR}/src/module.c 11 | ) 12 | 13 | target_include_directories(usermod_lvgl_esp32 INTERFACE 14 | ${IDF_PATH}/components/esp_lcd/include/ 15 | ${CMAKE_CURRENT_LIST_DIR}/binding/lvgl 16 | ${CMAKE_CURRENT_LIST_DIR}/binding/lvgl/src 17 | ) 18 | 19 | target_link_libraries(usermod_lvgl_esp32 INTERFACE lvgl_interface) 20 | 21 | target_link_libraries(usermod INTERFACE usermod_lvgl_esp32) 22 | -------------------------------------------------------------------------------- /examples/hello_world.py: -------------------------------------------------------------------------------- 1 | from .hardware import display 2 | 3 | import lvgl as lv 4 | import lvgl_esp32 5 | 6 | wrapper = lvgl_esp32.Wrapper(display) 7 | wrapper.init() 8 | 9 | screen = lv.screen_active() 10 | screen.set_style_bg_color(lv.color_hex(0x003a57), lv.PART.MAIN) 11 | 12 | label = lv.label(screen) 13 | label.set_text("Hello world from MicroPython") 14 | label.set_style_text_color(lv.color_hex(0xffffff), lv.PART.MAIN) 15 | label.align(lv.ALIGN.CENTER, 0, 0) 16 | 17 | a = lv.anim_t() 18 | a.init() 19 | a.set_var(label) 20 | a.set_values(10, 50) 21 | a.set_duration(1000) 22 | a.set_playback_delay(100) 23 | a.set_playback_duration(300) 24 | a.set_repeat_delay(500) 25 | a.set_repeat_count(lv.ANIM_REPEAT_INFINITE) 26 | a.set_path_cb(lv.anim_t.path_ease_in_out) 27 | a.set_custom_exec_cb(lambda _, v: label.set_y(v)) 28 | a.start() 29 | 30 | while True: 31 | lv.timer_handler_run_in_period(5) 32 | -------------------------------------------------------------------------------- /src/spi.h: -------------------------------------------------------------------------------- 1 | #ifndef __LVGL_ESP32_SPI_H__ 2 | #define __LVGL_ESP32_SPI_H__ 3 | 4 | #include "py/obj.h" 5 | 6 | #include "hal/spi_types.h" 7 | 8 | typedef struct lvgl_esp32_SPI_obj_t 9 | { 10 | mp_obj_base_t base; 11 | 12 | spi_host_device_t spi_host_device; 13 | uint32_t baudrate; 14 | 15 | uint8_t sck; 16 | uint8_t mosi; 17 | uint8_t miso; 18 | 19 | bool bus_initialized; 20 | bool needs_deinit; 21 | 22 | // This is a very hacky solution that will only work as long as the SPI devices are internal to this module 23 | // If this PR gets merged and released, this hack should be removed 24 | // https://github.com/espressif/esp-idf/pull/13856 25 | uint8_t device_count; 26 | } lvgl_esp32_SPI_obj_t; 27 | 28 | void lvgl_esp32_SPI_internal_deinit(lvgl_esp32_SPI_obj_t* self); 29 | 30 | extern const mp_obj_type_t lvgl_esp32_SPI_type; 31 | 32 | #endif /* __LVGL_ESP32_SPI_H__ */ 33 | -------------------------------------------------------------------------------- /src/display.h: -------------------------------------------------------------------------------- 1 | #ifndef __LVGL_ESP32_DISPLAY_H__ 2 | #define __LVGL_ESP32_DISPLAY_H__ 3 | 4 | #include "spi.h" 5 | 6 | #include "esp_lcd_types.h" 7 | #include "py/obj.h" 8 | 9 | typedef void (*lvgl_esp32_transfer_done_cb_t)(void *); 10 | 11 | typedef struct lvgl_esp32_Display_obj_t 12 | { 13 | mp_obj_base_t base; 14 | 15 | uint16_t width; 16 | uint16_t height; 17 | 18 | lvgl_esp32_SPI_obj_t *spi; 19 | uint8_t reset; 20 | uint8_t dc; 21 | uint8_t cs; 22 | uint32_t pixel_clock; 23 | 24 | bool swap_xy; 25 | bool mirror_x; 26 | bool mirror_y; 27 | bool invert; 28 | bool bgr; 29 | 30 | lvgl_esp32_transfer_done_cb_t transfer_done_cb; 31 | void *transfer_done_user_data; 32 | 33 | esp_lcd_panel_handle_t panel; 34 | esp_lcd_panel_io_handle_t io_handle; 35 | } lvgl_esp32_Display_obj_t; 36 | 37 | extern const mp_obj_type_t lvgl_esp32_Display_type; 38 | 39 | void lvgl_esp32_Display_draw_bitmap( 40 | lvgl_esp32_Display_obj_t *self, 41 | int x_start, 42 | int y_start, 43 | int x_end, 44 | int y_end, 45 | const void *data 46 | ); 47 | 48 | #endif /* __LVGL_ESP32_DISPLAY_H__ */ 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mathy Vanvoorden 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /binding/include/lv_mp_mem_custom_include.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 LVGL 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef __LV_MP_MEM_CUSTOM_INCLUDE_H 27 | #define __LV_MP_MEM_CUSTOM_INCLUDE_H 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #endif //__LV_MP_MEM_CUSTOM_INCLUDE_H 34 | -------------------------------------------------------------------------------- /binding/binding.cmake: -------------------------------------------------------------------------------- 1 | find_package(Python3 REQUIRED COMPONENTS Interpreter) 2 | 3 | # Set some variables 4 | set(LVGL_BINDINGS_DIR ${CMAKE_CURRENT_LIST_DIR}) # Location of the binding 5 | set(LVGL_ROOT_DIR ${LVGL_BINDINGS_DIR}/lvgl) # Location of LVGL 6 | 7 | # Include the actual LVGL build, produces the lvgl_interface target 8 | include(${LVGL_ROOT_DIR}/env_support/cmake/micropython.cmake) 9 | 10 | # Set binding variables 11 | set(LVGL_MPY ${CMAKE_BINARY_DIR}/lv_mp.c) # Generated bindings 12 | set(LVGL_MPY_PP ${LVGL_MPY}.pp) # Preprocessed bindings 13 | set(LVGL_MPY_METADATA ${LVGL_MPY}.json) # Bindings metadata 14 | set(LVGL_LVGL_H ${LVGL_ROOT_DIR}/lvgl.h) # lvgl.h 15 | set(LVGL_GEN_MPY ${LVGL_BINDINGS_DIR}/gen/gen_mpy.py) # gen_mpy.py script 16 | set(LVGL_FAKE_LIBC ${LVGL_BINDINGS_DIR}/pycparser/utils/fake_libc_include) # Fake libc implementation 17 | 18 | # Gather the headers 19 | file(GLOB_RECURSE LVGL_HEADERS ${LVGL_ROOT_DIR}/src/*.h ${LVGL_BINDINGS_DIR}/lv_conf.h) 20 | 21 | # Preprocess the bindings 22 | add_custom_command( 23 | OUTPUT 24 | ${LVGL_MPY_PP} 25 | COMMAND 26 | ${CMAKE_C_COMPILER} 27 | -E 28 | -DPYCPARSER 29 | -I ${LVGL_FAKE_LIBC} 30 | ${MICROPY_CPP_FLAGS} 31 | ${LVGL_LVGL_H} 32 | > ${LVGL_MPY_PP} 33 | DEPENDS 34 | ${LVGL_LVGL_H} 35 | ${LVGL_HEADERS} 36 | ${LVGL_FAKE_LIBC} 37 | IMPLICIT_DEPENDS 38 | C ${LVGL_LVGL_H} 39 | VERBATIM 40 | COMMAND_EXPAND_LISTS 41 | ) 42 | 43 | # Actually generate the bindings 44 | add_custom_command( 45 | OUTPUT 46 | ${LVGL_MPY} 47 | COMMAND 48 | ${Python3_EXECUTABLE} 49 | ${LVGL_GEN_MPY} 50 | -M lvgl 51 | -MP lv 52 | -MD ${LVGL_MPY_METADATA} 53 | -E ${LVGL_MPY_PP} 54 | ${LVGL_LVGL_H} 55 | > ${LVGL_MPY} || (rm -f ${LVGL_MPY} && /bin/false) 56 | DEPENDS 57 | ${LVGL_GEN_MPY} 58 | ${LVGL_MPY_PP} 59 | COMMAND_EXPAND_LISTS 60 | ) 61 | 62 | # Unfortunately IDF requires all files to be present during configuration, but these only get written during the 63 | # build, so we temporarily write empty files so that IDF is happy 64 | if (NOT EXISTS ${LVGL_MPY}) 65 | file(WRITE ${LVGL_MPY} "") 66 | endif () 67 | 68 | add_library(usermod_lv_bindings INTERFACE) 69 | target_sources(usermod_lv_bindings INTERFACE ${LVGL_MPY}) 70 | target_include_directories(usermod_lv_bindings INTERFACE ${LVGL_BINDINGS_DIR}) 71 | 72 | target_link_libraries(usermod_lv_bindings INTERFACE lvgl_interface) 73 | 74 | # make usermod (target declared by Micropython for all user compiled modules) link to bindings 75 | # this way the bindings (and transitively lvgl_interface) get proper compilation flags 76 | target_link_libraries(usermod INTERFACE usermod_lv_bindings) 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LVGL on ESP32 with MicroPython (as a USER_C_MODULE) 2 | 3 | This repository is an attempt to create an out-of-the-box LVGL experience on ESP32 files using MicroPython. 4 | 5 | ## Rationale 6 | 7 | While there are a lot of libraries, examples and information available on getting LVGL working on MicroPython there is a 8 | huge amount of fragmented, outdated and non-working code out there. 9 | 10 | After spending several days getting increasingly frustrated about the state of things I decided to fragment a bit more 11 | and create my own module with the following requirements: 12 | 13 | - Must compile as a USER_C_MODULE 14 | - Works with latest release of LVGL 15 | - Works with latest release of MicroPython 16 | - Works with latest (supported by MicroPython) release of ESP-IDF 17 | - Uses the `esp_lcd` driver of ESP-IDF with an SPI bus 18 | - Allows sharing SPI for example with SD card 19 | 20 | There are currently no plans to support 21 | - non-ESP32 devices 22 | - other than the standard `esp_lcd` displays 23 | - other buses than SPI 24 | - other displays than ST7789 as I do not possess any to test 25 | 26 | The final objective of this project is to support the camp badges for [Fri3d Camp](https://www.fri3d.be) and as such 27 | certain functionality you'd expect from a display driver might be missing as they are not necessary for that project. 28 | 29 | Don't hesitate to create a pull request to add the required functionality. 30 | 31 | ## Board configuration 32 | 33 | This wrapper requires that you activate the following options in your `sdkconfig.board`: 34 | 35 | ``` 36 | # lv_tick_inc is called from an esp_timer configured with interrupts 37 | CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y 38 | ``` 39 | ## Building 40 | 41 | First follow the MicroPython documentation to create a build for your device without this module and verify it is 42 | working. 43 | 44 | Once you are sure it does, clone the repository with recursive submodules, this makes sure 45 | 46 | ```shell 47 | git clone --recurse-submodules 48 | ``` 49 | 50 | Then you need to alter your partition table and add it into your `sdkconfig.board`, app partition (where binary micropython.bin exist) size need 0x260000 at least(depend on your project's manifest), following is an example partition table with 16MiB flash: 51 | ``` 52 | # Name, Type, SubType, Offset, Size, Flags 53 | nvs, data, nvs, 0x9000, 0x6000, 54 | phy_init, data, phy, 0xf000, 0x1000, 55 | factory, app, factory, 0x10000, 0x2F0000, 56 | vfs, data, fat, 0x300000, 0x700000, 57 | ``` 58 | Compile adding the `USER_C_MODULES` parameter to the `make` command. 59 | 60 | ```shell 61 | make USER_C_MODULES=/path/to/lvgl_esp32_mpy/micropython.cmake 62 | ``` 63 | 64 | ## Broken things 65 | 66 | * [Soft-reboots are not working](https://github.com/lvgl/lv_binding_micropython/issues/343) 67 | 68 | ## Missing things 69 | 70 | Things I'll probably still implement at some point: 71 | 72 | - Rotation of displays 73 | 74 | ## Supported versions 75 | 76 | - LVGL: 9.1 77 | - MicroPython: 1.23.0 78 | - ESP-IDF: 5.2.2 79 | 80 | ## Acknowledgements 81 | 82 | A lot of ideas and/or work were borrowed from 83 | 84 | - [kdschlosser](https://github.com/kdschlosser/lvgl_micropython)'s LVGL binding 85 | - [russhughes](https://github.com/russhughes/s3lcd)'s s3lcd 86 | - The original [lv_binding_micropython](https://github.com/lvgl/lv_binding_micropython) 87 | -------------------------------------------------------------------------------- /src/spi.c: -------------------------------------------------------------------------------- 1 | #include "spi.h" 2 | 3 | #include "py/runtime.h" 4 | 5 | #include "driver/spi_master.h" 6 | #include "esp_err.h" 7 | #include "esp_log.h" 8 | 9 | static const char *TAG = "lvgl_esp32_spi"; 10 | 11 | static mp_obj_t lvgl_esp32_SPI_init(mp_obj_t self_ptr) 12 | { 13 | struct lvgl_esp32_SPI_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 14 | 15 | ESP_LOGI(TAG, "Initializing SPI Bus"); 16 | spi_bus_config_t bus_config = { 17 | .sclk_io_num = self->sck, 18 | .mosi_io_num = self->mosi, 19 | .miso_io_num = self->miso, 20 | .quadwp_io_num = -1, 21 | .quadhd_io_num = -1, 22 | }; 23 | 24 | ESP_ERROR_CHECK(spi_bus_initialize(self->spi_host_device, &bus_config, SPI_DMA_CH_AUTO)); 25 | self->bus_initialized = true; 26 | 27 | return mp_obj_new_int_from_uint(0); 28 | } 29 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_SPI_init_obj, lvgl_esp32_SPI_init); 30 | 31 | static mp_obj_t lvgl_esp32_SPI_deinit(mp_obj_t self_ptr) 32 | { 33 | struct lvgl_esp32_SPI_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 34 | 35 | if (self->bus_initialized) 36 | { 37 | ESP_LOGI(TAG, "Deinitializing SPI Bus"); 38 | 39 | // HACK 40 | if (self->device_count > 0) 41 | { 42 | ESP_LOGW(TAG, "Could not deinitialize SPI Bus (yet), still active devices"); 43 | self->needs_deinit = true; 44 | return mp_obj_new_int_from_uint(0); 45 | } 46 | 47 | esp_err_t result = spi_bus_free(self->spi_host_device); 48 | 49 | if (result == ESP_ERR_INVALID_STATE) 50 | { 51 | // We can not predict the order in which MicroPython destroys objects, so we allow deinit to be called 52 | // multiple times from the bus users as they deinit() too. 53 | ESP_LOGW(TAG, "Could not deinitialize SPI Bus (yet), still active devices"); 54 | self->needs_deinit = true; 55 | } 56 | else 57 | { 58 | ESP_ERROR_CHECK(result); 59 | self->bus_initialized = false; 60 | self->needs_deinit = false; 61 | } 62 | } 63 | 64 | return mp_obj_new_int_from_uint(0); 65 | } 66 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_SPI_deinit_obj, lvgl_esp32_SPI_deinit); 67 | 68 | void lvgl_esp32_SPI_internal_deinit(lvgl_esp32_SPI_obj_t *self) 69 | { 70 | if (self->needs_deinit) 71 | { 72 | lvgl_esp32_SPI_deinit(self); 73 | } 74 | } 75 | 76 | static mp_obj_t lvgl_esp32_SPI_make_new( 77 | const mp_obj_type_t *type, 78 | size_t n_args, 79 | size_t n_kw, 80 | const mp_obj_t *all_args 81 | ) 82 | { 83 | enum 84 | { 85 | ARG_spi_id, // ID of SPI to use 86 | ARG_baudrate, // Baudrate 87 | ARG_sck, // SCK pin 88 | ARG_mosi, // MOSI pin 89 | ARG_miso, // MISO pin 90 | }; 91 | 92 | static const mp_arg_t allowed_args[] = { 93 | { MP_QSTR_spi_id, MP_ARG_INT | MP_ARG_REQUIRED }, 94 | { MP_QSTR_baudrate, MP_ARG_INT | MP_ARG_REQUIRED }, 95 | { MP_QSTR_sck, MP_ARG_INT | MP_ARG_REQUIRED }, 96 | { MP_QSTR_mosi, MP_ARG_INT | MP_ARG_REQUIRED }, 97 | { MP_QSTR_miso, MP_ARG_INT | MP_ARG_REQUIRED }, 98 | }; 99 | 100 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 101 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 102 | 103 | struct lvgl_esp32_SPI_obj_t *self = mp_obj_malloc_with_finaliser(lvgl_esp32_SPI_obj_t, &lvgl_esp32_SPI_type); 104 | 105 | switch (args[ARG_spi_id].u_int) 106 | { 107 | case 1: 108 | self->spi_host_device = SPI1_HOST; 109 | break; 110 | case 2: 111 | self->spi_host_device = SPI2_HOST; 112 | break; 113 | case 3: 114 | self->spi_host_device = SPI3_HOST; 115 | break; 116 | } 117 | self->baudrate = args[ARG_baudrate].u_int; 118 | 119 | self->sck = args[ARG_sck].u_int; 120 | self->mosi = args[ARG_mosi].u_int; 121 | self->miso = args[ARG_miso].u_int; 122 | 123 | self->bus_initialized = false; 124 | self->needs_deinit = false; 125 | self->device_count = 0; 126 | 127 | return MP_OBJ_FROM_PTR(self); 128 | } 129 | 130 | static const mp_rom_map_elem_t lvgl_esp32_SPI_locals_table[] = { 131 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&lvgl_esp32_SPI_init_obj) }, 132 | { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lvgl_esp32_SPI_deinit_obj) }, 133 | { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&lvgl_esp32_SPI_deinit_obj) }, 134 | }; 135 | 136 | static MP_DEFINE_CONST_DICT(lvgl_esp32_SPI_locals, lvgl_esp32_SPI_locals_table); 137 | 138 | MP_DEFINE_CONST_OBJ_TYPE( 139 | lvgl_esp32_SPI_type, 140 | MP_QSTR_SPI, 141 | MP_TYPE_FLAG_NONE, 142 | make_new, 143 | lvgl_esp32_SPI_make_new, 144 | locals_dict, 145 | &lvgl_esp32_SPI_locals 146 | ); 147 | -------------------------------------------------------------------------------- /src/wrapper.c: -------------------------------------------------------------------------------- 1 | #include "wrapper.h" 2 | 3 | #include "esp_log.h" 4 | #include "esp_timer.h" 5 | #include "py/runtime.h" 6 | 7 | static const char *TAG = "lvgl_esp32_wrapper"; 8 | 9 | static void flush_cb(lv_display_t *display, const lv_area_t *area, uint8_t *data) 10 | { 11 | lvgl_esp32_Wrapper_obj_t *self = (lvgl_esp32_Wrapper_obj_t *) lv_display_get_user_data(display);; 12 | 13 | // Correct byte order 14 | lv_draw_sw_rgb565_swap(data, self->buf_size); 15 | 16 | // Blit to the screen 17 | lvgl_esp32_Display_draw_bitmap(self->display, area->x1, area->y1, area->x2 + 1, area->y2 + 1, data); 18 | } 19 | 20 | static void transfer_done_cb(void *user_data) 21 | { 22 | lvgl_esp32_Wrapper_obj_t *self = (lvgl_esp32_Wrapper_obj_t *) user_data; 23 | lv_disp_flush_ready(self->lv_display); 24 | } 25 | 26 | static uint32_t tick_get_cb() 27 | { 28 | return esp_timer_get_time() / 1000; 29 | } 30 | 31 | static mp_obj_t lvgl_esp32_Wrapper_init(mp_obj_t self_ptr) 32 | { 33 | lvgl_esp32_Wrapper_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 34 | 35 | ESP_LOGI(TAG, "Initializing LVGL Wrapper"); 36 | 37 | if (!lv_is_initialized()) 38 | { 39 | ESP_LOGI(TAG, "Initializing LVGL library"); 40 | lv_init(); 41 | } 42 | 43 | ESP_LOGI(TAG, "Initializing LVGL display with size %dx%d", self->display->width, self->display->height); 44 | self->lv_display = lv_display_create(self->display->width, self->display->height); 45 | 46 | ESP_LOGI(TAG, "Creating display buffers"); 47 | self->buf_size = self->display->width * 20; 48 | self->buf1 = heap_caps_malloc(self->buf_size * sizeof(lv_color_t), MALLOC_CAP_DMA); 49 | assert(self->buf1); 50 | self->buf2 = heap_caps_malloc(self->buf_size * sizeof(lv_color_t), MALLOC_CAP_DMA); 51 | assert(self->buf2); 52 | 53 | // initialize LVGL draw buffers 54 | lv_display_set_buffers(self->lv_display, self->buf1, self->buf2, self->buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL); 55 | 56 | ESP_LOGI(TAG, "Registering callback functions"); 57 | self->display->transfer_done_cb = transfer_done_cb; 58 | self->display->transfer_done_user_data = (void *) self; 59 | lv_display_set_flush_cb(self->lv_display, flush_cb); 60 | lv_display_set_user_data(self->lv_display, self); 61 | lv_tick_set_cb(tick_get_cb); 62 | 63 | return mp_obj_new_int_from_uint(0); 64 | } 65 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_Wrapper_init_obj, lvgl_esp32_Wrapper_init); 66 | 67 | static mp_obj_t lvgl_esp32_Wrapper_deinit(mp_obj_t self_ptr) 68 | { 69 | lvgl_esp32_Wrapper_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 70 | 71 | ESP_LOGI(TAG, "Deinitializing LVGL Wrapper"); 72 | 73 | ESP_LOGI(TAG, "Disabling callback functions"); 74 | lv_tick_set_cb(NULL); 75 | self->display->transfer_done_cb = NULL; 76 | self->display->transfer_done_user_data = NULL; 77 | 78 | if (self->lv_display != NULL) 79 | { 80 | ESP_LOGI(TAG, "Deleting LVGL display"); 81 | lv_display_delete(self->lv_display); 82 | self->lv_display = NULL; 83 | } 84 | 85 | self->buf_size = 0; 86 | if (self->buf1 != NULL) 87 | { 88 | ESP_LOGI(TAG, "Freeing first display buffer"); 89 | heap_caps_free(self->buf1); 90 | self->buf1 = NULL; 91 | } 92 | if (self->buf2 != NULL) 93 | { 94 | ESP_LOGI(TAG, "Freeing second display buffer"); 95 | heap_caps_free(self->buf2); 96 | self->buf2 = NULL; 97 | } 98 | 99 | if (lv_is_initialized()) 100 | { 101 | ESP_LOGI(TAG, "Deinitializing LVGL"); 102 | lv_deinit(); 103 | } 104 | 105 | return mp_obj_new_int_from_uint(0); 106 | } 107 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_Wrapper_deinit_obj, lvgl_esp32_Wrapper_deinit); 108 | 109 | static mp_obj_t lvgl_esp32_Wrapper_make_new( 110 | const mp_obj_type_t *type, 111 | size_t n_args, 112 | size_t n_kw, 113 | const mp_obj_t *all_args 114 | ) 115 | { 116 | enum 117 | { 118 | ARG_display, // a display instance 119 | }; 120 | 121 | static const mp_arg_t allowed_args[] = { 122 | { MP_QSTR_display, MP_ARG_OBJ | MP_ARG_REQUIRED }, 123 | }; 124 | 125 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 126 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 127 | 128 | lvgl_esp32_Wrapper_obj_t *self = mp_obj_malloc_with_finaliser(lvgl_esp32_Wrapper_obj_t, &lvgl_esp32_Wrapper_type); 129 | 130 | if (mp_obj_get_type(args[ARG_display].u_obj) != &lvgl_esp32_Display_type) 131 | { 132 | mp_raise_ValueError(MP_ERROR_TEXT("Expecting a Display object")); 133 | } 134 | 135 | self->display = (lvgl_esp32_Display_obj_t *) MP_OBJ_TO_PTR(args[ARG_display].u_obj); 136 | 137 | self->buf_size = 0; 138 | self->buf1 = NULL; 139 | self->buf2 = NULL; 140 | 141 | self->lv_display = NULL; 142 | 143 | return MP_OBJ_FROM_PTR(self); 144 | } 145 | 146 | static const mp_rom_map_elem_t lvgl_esp32_Wrapper_locals_table[] = { 147 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&lvgl_esp32_Wrapper_init_obj) }, 148 | { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lvgl_esp32_Wrapper_deinit_obj) }, 149 | { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&lvgl_esp32_Wrapper_deinit_obj) }, 150 | }; 151 | 152 | static MP_DEFINE_CONST_DICT(lvgl_esp32_Wrapper_locals, lvgl_esp32_Wrapper_locals_table); 153 | 154 | MP_DEFINE_CONST_OBJ_TYPE( 155 | lvgl_esp32_Wrapper_type, 156 | MP_QSTR_Wrapper, 157 | MP_TYPE_FLAG_NONE, 158 | make_new, 159 | lvgl_esp32_Wrapper_make_new, 160 | locals_dict, 161 | &lvgl_esp32_Wrapper_locals 162 | ); 163 | -------------------------------------------------------------------------------- /src/display.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | 3 | #include "py/runtime.h" 4 | 5 | #include 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/semphr.h" 9 | #include "esp_timer.h" 10 | #include "esp_lcd_panel_io.h" 11 | #include "esp_lcd_panel_vendor.h" 12 | #include "esp_lcd_panel_ops.h" 13 | #include "driver/gpio.h" 14 | #include "driver/spi_master.h" 15 | #include "esp_err.h" 16 | #include "esp_log.h" 17 | 18 | static const char *TAG = "lvgl_esp32_display"; 19 | 20 | // Bit number used to represent command and parameter 21 | #define LCD_CMD_BITS 8 22 | #define LCD_PARAM_BITS 8 23 | 24 | static bool on_color_trans_done_cb( 25 | esp_lcd_panel_io_handle_t panel_io, 26 | esp_lcd_panel_io_event_data_t *edata, 27 | void *user_ctx 28 | ) 29 | { 30 | lvgl_esp32_Display_obj_t *self = (lvgl_esp32_Display_obj_t *) user_ctx; 31 | 32 | if (self->transfer_done_cb != NULL) 33 | { 34 | self->transfer_done_cb(self->transfer_done_user_data); 35 | } 36 | 37 | return false; 38 | } 39 | 40 | void lvgl_esp32_Display_draw_bitmap( 41 | lvgl_esp32_Display_obj_t *self, 42 | int x_start, 43 | int y_start, 44 | int x_end, 45 | int y_end, 46 | const void *data 47 | ) 48 | { 49 | ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(self->panel, x_start, y_start, x_end, y_end, data)); 50 | } 51 | 52 | static void clear(lvgl_esp32_Display_obj_t *self) 53 | { 54 | ESP_LOGI(TAG, "Clearing screen"); 55 | 56 | // Create a temporary empty buffer of only one line of pixels so this will also work on memory-constrained devices 57 | size_t buf_size = self->width; 58 | uint16_t *buf = heap_caps_calloc(1, buf_size * sizeof(uint16_t), MALLOC_CAP_DMA); 59 | 60 | assert(buf); 61 | 62 | // Blit lines to the screen 63 | for (int line = 0; line < self->height; line++) 64 | { 65 | lvgl_esp32_Display_draw_bitmap(self, 0, line, self->width, line + 1, buf); 66 | } 67 | 68 | // Release the buffer 69 | heap_caps_free(buf); 70 | } 71 | 72 | static mp_obj_t lvgl_esp32_Display_init(mp_obj_t self_ptr) 73 | { 74 | lvgl_esp32_Display_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 75 | 76 | ESP_LOGI(TAG, "Setting up panel IO"); 77 | esp_lcd_panel_io_spi_config_t io_config = { 78 | .dc_gpio_num = self->dc, 79 | .cs_gpio_num = self->cs, 80 | .pclk_hz = self->pixel_clock, 81 | .lcd_cmd_bits = LCD_CMD_BITS, 82 | .lcd_param_bits = LCD_PARAM_BITS, 83 | .spi_mode = 0, 84 | .trans_queue_depth = 10, 85 | .on_color_trans_done = on_color_trans_done_cb, 86 | .user_ctx = self, 87 | }; 88 | 89 | ESP_ERROR_CHECK( 90 | esp_lcd_new_panel_io_spi( 91 | (esp_lcd_spi_bus_handle_t) self->spi->spi_host_device, 92 | &io_config, 93 | &self->io_handle 94 | ) 95 | ); 96 | 97 | // HACK 98 | self->spi->device_count++; 99 | 100 | ESP_LOGI(TAG, "Setting up ST7789 panel driver"); 101 | esp_lcd_panel_dev_config_t panel_config = { 102 | .reset_gpio_num = self->reset, 103 | .rgb_ele_order = self->bgr ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB, 104 | .bits_per_pixel = 16, 105 | }; 106 | 107 | ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(self->io_handle, &panel_config, &self->panel)); 108 | ESP_ERROR_CHECK(esp_lcd_panel_reset(self->panel)); 109 | ESP_ERROR_CHECK(esp_lcd_panel_init(self->panel)); 110 | 111 | ESP_ERROR_CHECK(esp_lcd_panel_invert_color(self->panel, self->invert)); 112 | ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(self->panel, self->swap_xy)); 113 | ESP_ERROR_CHECK(esp_lcd_panel_mirror(self->panel, self->mirror_x, self->mirror_y)); 114 | 115 | clear(self); 116 | 117 | ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(self->panel, true)); 118 | 119 | return mp_obj_new_int_from_uint(0); 120 | } 121 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_Display_init_obj, lvgl_esp32_Display_init); 122 | 123 | static mp_obj_t lvgl_esp32_Display_deinit(mp_obj_t self_ptr) 124 | { 125 | lvgl_esp32_Display_obj_t *self = MP_OBJ_TO_PTR(self_ptr); 126 | 127 | if(self->panel != NULL) 128 | { 129 | ESP_LOGI(TAG, "Deinitializing ST7789 panel driver"); 130 | ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(self->panel, false)); 131 | ESP_ERROR_CHECK(esp_lcd_panel_del(self->panel)); 132 | self->panel = NULL; 133 | } 134 | 135 | if(self->io_handle != NULL) 136 | { 137 | ESP_LOGI(TAG, "Deinitializing panel IO"); 138 | ESP_ERROR_CHECK(esp_lcd_panel_io_del(self->io_handle)); 139 | self->io_handle = NULL; 140 | 141 | // HACK 142 | self->spi->device_count--; 143 | 144 | // We call deinit on spi in case it was (unsuccessfully) deleted earlier 145 | lvgl_esp32_SPI_internal_deinit(self->spi); 146 | } 147 | 148 | return mp_obj_new_int_from_uint(0); 149 | } 150 | static MP_DEFINE_CONST_FUN_OBJ_1(lvgl_esp32_Display_deinit_obj, lvgl_esp32_Display_deinit); 151 | 152 | static mp_obj_t lvgl_esp32_Display_make_new( 153 | const mp_obj_type_t *type, 154 | size_t n_args, 155 | size_t n_kw, 156 | const mp_obj_t *all_args 157 | ) 158 | { 159 | enum 160 | { 161 | ARG_width, // width of the display 162 | ARG_height, // height of the display 163 | ARG_spi, // configured SPI instance 164 | ARG_reset, // RESET pin number 165 | ARG_dc, // DC pin number 166 | ARG_cs, // CS pin number 167 | ARG_pixel_clock, // Pixel clock in Hz 168 | ARG_swap_xy, // swap X and Y axis 169 | ARG_mirror_x, // mirror on X axis 170 | ARG_mirror_y, // mirror on Y axis 171 | ARG_invert, // invert colors 172 | ARG_bgr, // use BGR element order 173 | }; 174 | 175 | static const mp_arg_t allowed_args[] = { 176 | { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED }, 177 | { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED }, 178 | { MP_QSTR_spi, MP_ARG_OBJ | MP_ARG_REQUIRED }, 179 | { MP_QSTR_reset, MP_ARG_INT | MP_ARG_REQUIRED }, 180 | { MP_QSTR_dc, MP_ARG_INT | MP_ARG_REQUIRED }, 181 | { MP_QSTR_cs, MP_ARG_INT | MP_ARG_REQUIRED }, 182 | { MP_QSTR_pixel_clock, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 20 * 1000 * 1000 }}, 183 | { MP_QSTR_swap_xy, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false }}, 184 | { MP_QSTR_mirror_x, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false }}, 185 | { MP_QSTR_mirror_y, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false }}, 186 | { MP_QSTR_invert, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false }}, 187 | { MP_QSTR_bgr, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false }}, 188 | }; 189 | 190 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 191 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 192 | 193 | lvgl_esp32_Display_obj_t *self = mp_obj_malloc_with_finaliser(lvgl_esp32_Display_obj_t, &lvgl_esp32_Display_type); 194 | 195 | self->width = args[ARG_width].u_int; 196 | self->height = args[ARG_height].u_int; 197 | 198 | self->spi = (lvgl_esp32_SPI_obj_t *) MP_OBJ_TO_PTR(args[ARG_spi].u_obj); 199 | self->reset = args[ARG_reset].u_int; 200 | self->dc = args[ARG_dc].u_int; 201 | self->cs = args[ARG_cs].u_int; 202 | self->pixel_clock = args[ARG_pixel_clock].u_int; 203 | 204 | self->swap_xy = args[ARG_swap_xy].u_bool; 205 | self->mirror_x = args[ARG_mirror_x].u_bool; 206 | self->mirror_y = args[ARG_mirror_y].u_bool; 207 | self->invert = args[ARG_invert].u_bool; 208 | self->bgr = args[ARG_bgr].u_bool; 209 | 210 | self->transfer_done_cb = NULL; 211 | self->transfer_done_user_data = NULL; 212 | 213 | self->panel = NULL; 214 | self->io_handle = NULL; 215 | 216 | return MP_OBJ_FROM_PTR(self); 217 | } 218 | 219 | static const mp_rom_map_elem_t lvgl_esp32_Display_locals_table[] = { 220 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&lvgl_esp32_Display_init_obj) }, 221 | { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lvgl_esp32_Display_deinit_obj) }, 222 | { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&lvgl_esp32_Display_deinit_obj) }, 223 | }; 224 | 225 | static MP_DEFINE_CONST_DICT(lvgl_esp32_Display_locals, lvgl_esp32_Display_locals_table); 226 | 227 | MP_DEFINE_CONST_OBJ_TYPE( 228 | lvgl_esp32_Display_type, 229 | MP_QSTR_Display, 230 | MP_TYPE_FLAG_NONE, 231 | make_new, 232 | lvgl_esp32_Display_make_new, 233 | locals_dict, 234 | &lvgl_esp32_Display_locals 235 | ); 236 | -------------------------------------------------------------------------------- /binding/lv_conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 LVGL 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | /** 26 | * @file lv_conf.h 27 | * Configuration file for v9.1.0 28 | */ 29 | 30 | /* 31 | * Copy this file as `lv_conf.h` 32 | * 1. simply next to the `lvgl` folder 33 | * 2. or any other places and 34 | * - define `LV_CONF_INCLUDE_SIMPLE` 35 | * - add the path as include path 36 | */ 37 | 38 | /* clang-format off */ 39 | #if 1 /*Set it to "1" to enable content*/ 40 | 41 | #ifndef LV_CONF_H 42 | #define LV_CONF_H 43 | 44 | /*If you need to include anything here, do it inside the `__ASSEMBLY__` guard */ 45 | #if 0 && defined(__ASSEMBLY__) 46 | #include "my_include.h" 47 | #endif 48 | 49 | /*==================== 50 | COLOR SETTINGS 51 | *====================*/ 52 | 53 | /*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ 54 | #define LV_COLOR_DEPTH 16 55 | 56 | /*========================= 57 | STDLIB WRAPPER SETTINGS 58 | *=========================*/ 59 | 60 | /* Possible values 61 | * - LV_STDLIB_BUILTIN: LVGL's built in implementation 62 | * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc 63 | * - LV_STDLIB_MICROPYTHON: MicroPython implementation 64 | * - LV_STDLIB_RTTHREAD: RT-Thread implementation 65 | * - LV_STDLIB_CUSTOM: Implement the functions externally 66 | */ 67 | #define LV_USE_STDLIB_MALLOC LV_STDLIB_MICROPYTHON 68 | #define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN 69 | #define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN 70 | 71 | #define LV_STDINT_INCLUDE 72 | #define LV_STDDEF_INCLUDE 73 | #define LV_STDBOOL_INCLUDE 74 | #define LV_INTTYPES_INCLUDE 75 | #define LV_LIMITS_INCLUDE 76 | #define LV_STDARG_INCLUDE 77 | 78 | #if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN 79 | /*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ 80 | #define LV_MEM_SIZE (64 * 1024U) /*[bytes]*/ 81 | 82 | /*Size of the memory expand for `lv_malloc()` in bytes*/ 83 | #define LV_MEM_POOL_EXPAND_SIZE 0 84 | 85 | /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ 86 | #define LV_MEM_ADR 0 /*0: unused*/ 87 | /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ 88 | #if LV_MEM_ADR == 0 89 | #undef LV_MEM_POOL_INCLUDE 90 | #undef LV_MEM_POOL_ALLOC 91 | #endif 92 | #endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/ 93 | 94 | /*==================== 95 | HAL SETTINGS 96 | *====================*/ 97 | 98 | /*Default display refresh, input device read and animation step period.*/ 99 | #define LV_DEF_REFR_PERIOD 33 /*[ms]*/ 100 | 101 | /*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. 102 | *(Not so important, you can adjust it to modify default sizes and spaces)*/ 103 | #define LV_DPI_DEF 130 /*[px/inch]*/ 104 | 105 | /*================= 106 | * OPERATING SYSTEM 107 | *=================*/ 108 | /*Select an operating system to use. Possible options: 109 | * - LV_OS_NONE 110 | * - LV_OS_PTHREAD 111 | * - LV_OS_FREERTOS 112 | * - LV_OS_CMSIS_RTOS2 113 | * - LV_OS_RTTHREAD 114 | * - LV_OS_WINDOWS 115 | * - LV_OS_CUSTOM */ 116 | #define LV_USE_OS LV_OS_NONE 117 | 118 | #if LV_USE_OS == LV_OS_CUSTOM 119 | #define LV_OS_CUSTOM_INCLUDE 120 | #endif 121 | 122 | /*======================== 123 | * RENDERING CONFIGURATION 124 | *========================*/ 125 | 126 | /*Align the stride of all layers and images to this bytes*/ 127 | #define LV_DRAW_BUF_STRIDE_ALIGN 1 128 | 129 | /*Align the start address of draw_buf addresses to this bytes*/ 130 | #define LV_DRAW_BUF_ALIGN 4 131 | 132 | /* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode 133 | * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. 134 | * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers 135 | * and can't be drawn in chunks. */ 136 | 137 | /*The target buffer size for simple layer chunks.*/ 138 | #define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /*[bytes]*/ 139 | 140 | #define LV_USE_DRAW_SW 1 141 | #if LV_USE_DRAW_SW == 1 142 | /* Set the number of draw unit. 143 | * > 1 requires an operating system enabled in `LV_USE_OS` 144 | * > 1 means multiply threads will render the screen in parallel */ 145 | #define LV_DRAW_SW_DRAW_UNIT_CNT 1 146 | 147 | /* Use Arm-2D to accelerate the sw render */ 148 | #define LV_USE_DRAW_ARM2D_SYNC 0 149 | 150 | /* Enable native helium assembly to be compiled */ 151 | #define LV_USE_NATIVE_HELIUM_ASM 0 152 | 153 | /* 0: use a simple renderer capable of drawing only simple rectangles with gradient, images, texts, and straight lines only 154 | * 1: use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too */ 155 | #define LV_DRAW_SW_COMPLEX 1 156 | 157 | #if LV_DRAW_SW_COMPLEX == 1 158 | /*Allow buffering some shadow calculation. 159 | *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` 160 | *Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost*/ 161 | #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 162 | 163 | /* Set number of maximally cached circle data. 164 | * The circumference of 1/4 circle are saved for anti-aliasing 165 | * radius * 4 bytes are used per circle (the most often used radiuses are saved) 166 | * 0: to disable caching */ 167 | #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 168 | #endif 169 | 170 | #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE 171 | 172 | #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM 173 | #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" 174 | #endif 175 | #endif 176 | 177 | /* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ 178 | #define LV_USE_DRAW_VGLITE 0 179 | 180 | #if LV_USE_DRAW_VGLITE 181 | /* Enable blit quality degradation workaround recommended for screen's dimension > 352 pixels. */ 182 | #define LV_USE_VGLITE_BLIT_SPLIT 0 183 | 184 | #if LV_USE_OS 185 | /* Enable VGLite draw async. Queue multiple tasks and flash them once to the GPU. */ 186 | #define LV_USE_VGLITE_DRAW_ASYNC 1 187 | #endif 188 | 189 | /* Enable VGLite asserts. */ 190 | #define LV_USE_VGLITE_ASSERT 0 191 | #endif 192 | 193 | /* Use NXP's PXP on iMX RTxxx platforms. */ 194 | #define LV_USE_DRAW_PXP 0 195 | 196 | #if LV_USE_DRAW_PXP 197 | /* Enable PXP asserts. */ 198 | #define LV_USE_PXP_ASSERT 0 199 | #endif 200 | 201 | /* Use Renesas Dave2D on RA platforms. */ 202 | #define LV_USE_DRAW_DAVE2D 0 203 | 204 | /* Draw using cached SDL textures*/ 205 | #define LV_USE_DRAW_SDL 0 206 | 207 | /* Use VG-Lite GPU. */ 208 | #define LV_USE_DRAW_VG_LITE 0 209 | 210 | #if LV_USE_DRAW_VG_LITE 211 | /* Enable VG-Lite custom external 'gpu_init()' function */ 212 | #define LV_VG_LITE_USE_GPU_INIT 0 213 | 214 | /* Enable VG-Lite assert. */ 215 | #define LV_VG_LITE_USE_ASSERT 0 216 | 217 | /* VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */ 218 | #define LV_VG_LITE_FLUSH_MAX_COUNT 8 219 | 220 | /* Enable border to simulate shadow 221 | * NOTE: which usually improves performance, 222 | * but does not guarantee the same rendering quality as the software. */ 223 | #define LV_VG_LITE_USE_BOX_SHADOW 0 224 | 225 | /* VG-Lite linear gradient image maximum cache number. 226 | * NOTE: The memory usage of a single gradient image is 4K bytes. 227 | */ 228 | #define LV_VG_LITE_LINEAER_GRAD_CACHE_CNT 32 229 | 230 | /* VG-Lite radial gradient image maximum cache size. 231 | * NOTE: The memory usage of a single gradient image is radial grad radius * 4 bytes. 232 | */ 233 | #define LV_VG_LITE_RADIAL_GRAD_CACHE_CNT 32 234 | 235 | #endif 236 | 237 | /*======================= 238 | * FEATURE CONFIGURATION 239 | *=======================*/ 240 | 241 | /*------------- 242 | * Logging 243 | *-----------*/ 244 | 245 | /*Enable the log module*/ 246 | #ifdef MICROPY_LV_USE_LOG 247 | #define LV_USE_LOG MICROPY_LV_USE_LOG 248 | #else 249 | #define LV_USE_LOG 0 250 | #endif 251 | 252 | #if LV_USE_LOG 253 | 254 | /*How important log should be added: 255 | *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information 256 | *LV_LOG_LEVEL_INFO Log important events 257 | *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem 258 | *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail 259 | *LV_LOG_LEVEL_USER Only logs added by the user 260 | *LV_LOG_LEVEL_NONE Do not log anything*/ 261 | #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN 262 | 263 | /*1: Print the log with 'printf'; 264 | *0: User need to register a callback with `lv_log_register_print_cb()`*/ 265 | #define LV_LOG_PRINTF 0 266 | 267 | /*1: Enable print timestamp; 268 | *0: Disable print timestamp*/ 269 | #define LV_LOG_USE_TIMESTAMP 1 270 | 271 | /*1: Print file and line number of the log; 272 | *0: Do not print file and line number of the log*/ 273 | #define LV_LOG_USE_FILE_LINE 1 274 | 275 | /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ 276 | #define LV_LOG_TRACE_MEM 1 277 | #define LV_LOG_TRACE_TIMER 1 278 | #define LV_LOG_TRACE_INDEV 1 279 | #define LV_LOG_TRACE_DISP_REFR 1 280 | #define LV_LOG_TRACE_EVENT 1 281 | #define LV_LOG_TRACE_OBJ_CREATE 1 282 | #define LV_LOG_TRACE_LAYOUT 1 283 | #define LV_LOG_TRACE_ANIM 1 284 | #define LV_LOG_TRACE_CACHE 1 285 | 286 | #endif /*LV_USE_LOG*/ 287 | 288 | /*------------- 289 | * Asserts 290 | *-----------*/ 291 | 292 | /*Enable asserts if an operation is failed or an invalid data is found. 293 | *If LV_USE_LOG is enabled an error message will be printed on failure*/ 294 | #define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ 295 | #define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ 296 | #define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ 297 | #define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ 298 | #define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ 299 | 300 | /*Add a custom handler when assert happens e.g. to restart the MCU*/ 301 | #define LV_ASSERT_HANDLER_INCLUDE 302 | #define LV_ASSERT_HANDLER while(1); /*Halt by default*/ 303 | 304 | /*------------- 305 | * Debug 306 | *-----------*/ 307 | 308 | /*1: Draw random colored rectangles over the redrawn areas*/ 309 | #define LV_USE_REFR_DEBUG 0 310 | 311 | /*1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/ 312 | #define LV_USE_LAYER_DEBUG 0 313 | 314 | /*1: Draw overlays with different colors for each draw_unit's tasks. 315 | *Also add the index number of the draw unit on white background. 316 | *For layers add the index number of the draw unit on black background.*/ 317 | #define LV_USE_PARALLEL_DRAW_DEBUG 0 318 | 319 | /*------------- 320 | * Others 321 | *-----------*/ 322 | 323 | /*Garbage Collector settings 324 | *Used if LVGL is bound to higher level language and the memory is managed by that language*/ 325 | extern void mp_lv_init_gc(); 326 | #define LV_GC_INIT() mp_lv_init_gc() 327 | 328 | #define LV_ENABLE_GLOBAL_CUSTOM 1 329 | #if LV_ENABLE_GLOBAL_CUSTOM 330 | extern void *mp_lv_roots; 331 | #define LV_GLOBAL_CUSTOM() ((lv_global_t*)mp_lv_roots) 332 | #endif 333 | 334 | /*Default cache size in bytes. 335 | *Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory. 336 | *If size is not set to 0, the decoder will fail to decode when the cache is full. 337 | *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ 338 | #ifdef MICROPY_CACHE_SIZE 339 | #define LV_CACHE_DEF_SIZE MICROPY_CACHE_SIZE 340 | #else 341 | #define LV_CACHE_DEF_SIZE 0 342 | #endif 343 | 344 | /*Default number of image header cache entries. The cache is used to store the headers of images 345 | *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ 346 | #ifdef MICROPY_IMAGE_HEADER_CACHE_COUNT 347 | #define LV_IMAGE_HEADER_CACHE_DEF_CNT MICROPY_IMAGE_HEADER_CACHE_COUNT 348 | #else 349 | #define LV_IMAGE_HEADER_CACHE_DEF_CNT 32 350 | #endif 351 | 352 | /*Number of stops allowed per gradient. Increase this to allow more stops. 353 | *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ 354 | #define LV_GRADIENT_MAX_STOPS 2 355 | 356 | /* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. 357 | * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ 358 | #define LV_COLOR_MIX_ROUND_OFS 0 359 | 360 | /* Add 2 x 32 bit variables to each lv_obj_t to speed up getting style properties */ 361 | #define LV_OBJ_STYLE_CACHE 1 362 | 363 | /* Add `id` field to `lv_obj_t` */ 364 | #define LV_USE_OBJ_ID 0 365 | 366 | /* Use lvgl builtin method for obj ID */ 367 | #define LV_USE_OBJ_ID_BUILTIN 0 368 | 369 | /*Use obj property set/get API*/ 370 | #define LV_USE_OBJ_PROPERTY 0 371 | 372 | /* VG-Lite Simulator */ 373 | /*Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */ 374 | #define LV_USE_VG_LITE_THORVG 0 375 | 376 | #if LV_USE_VG_LITE_THORVG 377 | 378 | /*Enable LVGL's blend mode support*/ 379 | #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0 380 | 381 | /*Enable YUV color format support*/ 382 | #define LV_VG_LITE_THORVG_YUV_SUPPORT 0 383 | 384 | /*Enable 16 pixels alignment*/ 385 | #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1 386 | 387 | /*Buffer address alignment*/ 388 | #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64 389 | 390 | /*Enable multi-thread render*/ 391 | #define LV_VG_LITE_THORVG_THREAD_RENDER 0 392 | 393 | #endif 394 | 395 | /*===================== 396 | * COMPILER SETTINGS 397 | *====================*/ 398 | 399 | /*For big endian systems set to 1*/ 400 | #define LV_BIG_ENDIAN_SYSTEM 0 401 | 402 | /*Define a custom attribute to `lv_tick_inc` function*/ 403 | #define LV_ATTRIBUTE_TICK_INC 404 | 405 | /*Define a custom attribute to `lv_timer_handler` function*/ 406 | #define LV_ATTRIBUTE_TIMER_HANDLER 407 | 408 | /*Define a custom attribute to `lv_display_flush_ready` function*/ 409 | #define LV_ATTRIBUTE_FLUSH_READY 410 | 411 | /*Required alignment size for buffers*/ 412 | #define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 413 | 414 | /*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). 415 | * E.g. __attribute__((aligned(4)))*/ 416 | #define LV_ATTRIBUTE_MEM_ALIGN 417 | 418 | /*Attribute to mark large constant arrays for example font's bitmaps*/ 419 | #define LV_ATTRIBUTE_LARGE_CONST 420 | 421 | /*Compiler prefix for a big array declaration in RAM*/ 422 | #define LV_ATTRIBUTE_LARGE_RAM_ARRAY 423 | 424 | /*Place performance critical functions into a faster memory (e.g RAM)*/ 425 | #define LV_ATTRIBUTE_FAST_MEM 426 | 427 | /*Export integer constant to binding. This macro is used with constants in the form of LV_ that 428 | *should also appear on LVGL binding API such as Micropython.*/ 429 | #define LV_EXPORT_CONST_INT(int_value) enum {ENUM_##int_value = int_value} 430 | 431 | /*Prefix all global extern data with this*/ 432 | #define LV_ATTRIBUTE_EXTERN_DATA 433 | 434 | /* Use `float` as `lv_value_precise_t` */ 435 | #define LV_USE_FLOAT 0 436 | 437 | /*================== 438 | * FONT USAGE 439 | *===================*/ 440 | 441 | /*Montserrat fonts with ASCII range and some symbols using bpp = 4 442 | *https://fonts.google.com/specimen/Montserrat*/ 443 | #define LV_FONT_MONTSERRAT_8 0 444 | #define LV_FONT_MONTSERRAT_10 0 445 | #define LV_FONT_MONTSERRAT_12 0 446 | #define LV_FONT_MONTSERRAT_14 1 447 | #define LV_FONT_MONTSERRAT_16 1 448 | #define LV_FONT_MONTSERRAT_18 0 449 | #define LV_FONT_MONTSERRAT_20 0 450 | #define LV_FONT_MONTSERRAT_22 0 451 | #define LV_FONT_MONTSERRAT_24 1 452 | #define LV_FONT_MONTSERRAT_26 0 453 | #define LV_FONT_MONTSERRAT_28 0 454 | #define LV_FONT_MONTSERRAT_30 0 455 | #define LV_FONT_MONTSERRAT_32 0 456 | #define LV_FONT_MONTSERRAT_34 0 457 | #define LV_FONT_MONTSERRAT_36 0 458 | #define LV_FONT_MONTSERRAT_38 0 459 | #define LV_FONT_MONTSERRAT_40 0 460 | #define LV_FONT_MONTSERRAT_42 0 461 | #define LV_FONT_MONTSERRAT_44 0 462 | #define LV_FONT_MONTSERRAT_46 0 463 | #define LV_FONT_MONTSERRAT_48 0 464 | 465 | /*Demonstrate special features*/ 466 | #define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ 467 | #define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ 468 | #define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ 469 | 470 | /*Pixel perfect monospace fonts*/ 471 | #define LV_FONT_UNSCII_8 0 472 | #define LV_FONT_UNSCII_16 0 473 | 474 | /*Optionally declare custom fonts here. 475 | *You can use these fonts as default font too and they will be available globally. 476 | *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ 477 | #define LV_FONT_CUSTOM_DECLARE 478 | 479 | /*Always set a default font*/ 480 | #define LV_FONT_DEFAULT &lv_font_montserrat_14 481 | 482 | /*Enable handling large font and/or fonts with a lot of characters. 483 | *The limit depends on the font size, font face and bpp. 484 | *Compiler error will be triggered if a font needs it.*/ 485 | #define LV_FONT_FMT_TXT_LARGE 0 486 | 487 | /*Enables/disables support for compressed fonts.*/ 488 | #define LV_USE_FONT_COMPRESSED 0 489 | 490 | /*Enable drawing placeholders when glyph dsc is not found*/ 491 | #define LV_USE_FONT_PLACEHOLDER 1 492 | 493 | /*================= 494 | * TEXT SETTINGS 495 | *=================*/ 496 | 497 | /** 498 | * Select a character encoding for strings. 499 | * Your IDE or editor should have the same character encoding 500 | * - LV_TXT_ENC_UTF8 501 | * - LV_TXT_ENC_ASCII 502 | */ 503 | #define LV_TXT_ENC LV_TXT_ENC_UTF8 504 | 505 | /*Can break (wrap) texts on these chars*/ 506 | #define LV_TXT_BREAK_CHARS " ,.;:-_)]}" 507 | 508 | /*If a word is at least this long, will break wherever "prettiest" 509 | *To disable, set to a value <= 0*/ 510 | #define LV_TXT_LINE_BREAK_LONG_LEN 0 511 | 512 | /*Minimum number of characters in a long word to put on a line before a break. 513 | *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ 514 | #define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 515 | 516 | /*Minimum number of characters in a long word to put on a line after a break. 517 | *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ 518 | #define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 519 | 520 | /*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. 521 | *The direction will be processed according to the Unicode Bidirectional Algorithm: 522 | *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ 523 | #define LV_USE_BIDI 0 524 | #if LV_USE_BIDI 525 | /*Set the default direction. Supported values: 526 | *`LV_BASE_DIR_LTR` Left-to-Right 527 | *`LV_BASE_DIR_RTL` Right-to-Left 528 | *`LV_BASE_DIR_AUTO` detect texts base direction*/ 529 | #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO 530 | #endif 531 | 532 | /*Enable Arabic/Persian processing 533 | *In these languages characters should be replaced with an other form based on their position in the text*/ 534 | #define LV_USE_ARABIC_PERSIAN_CHARS 0 535 | 536 | /*================== 537 | * WIDGETS 538 | *================*/ 539 | 540 | /*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ 541 | 542 | #define LV_WIDGETS_HAS_DEFAULT_VALUE 0 543 | 544 | #define LV_USE_ANIMIMG 1 545 | 546 | #define LV_USE_ARC 1 547 | 548 | #define LV_USE_BAR 1 549 | 550 | #define LV_USE_BUTTON 1 551 | 552 | #define LV_USE_BUTTONMATRIX 1 553 | 554 | #define LV_USE_CALENDAR 1 555 | #if LV_USE_CALENDAR 556 | #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 557 | #if LV_CALENDAR_WEEK_STARTS_MONDAY 558 | #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} 559 | #else 560 | #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} 561 | #endif 562 | 563 | #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} 564 | #define LV_USE_CALENDAR_HEADER_ARROW 1 565 | #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 566 | #endif /*LV_USE_CALENDAR*/ 567 | 568 | #define LV_USE_CANVAS 1 569 | 570 | #define LV_USE_CHART 1 571 | 572 | #define LV_USE_CHECKBOX 1 573 | 574 | #define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ 575 | 576 | #define LV_USE_IMAGE 1 /*Requires: lv_label*/ 577 | 578 | #define LV_USE_IMAGEBUTTON 1 579 | 580 | #define LV_USE_KEYBOARD 1 581 | 582 | #define LV_USE_LABEL 1 583 | #if LV_USE_LABEL 584 | #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ 585 | #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ 586 | #define LV_LABEL_WAIT_CHAR_COUNT 3 /*The count of wait chart*/ 587 | #endif 588 | 589 | #define LV_USE_LED 1 590 | 591 | #define LV_USE_LINE 1 592 | 593 | #define LV_USE_LIST 1 594 | 595 | #define LV_USE_MENU 1 596 | 597 | #define LV_USE_MSGBOX 1 598 | 599 | #define LV_USE_ROLLER 1 /*Requires: lv_label*/ 600 | 601 | #define LV_USE_SCALE 1 602 | 603 | #define LV_USE_SLIDER 1 /*Requires: lv_bar*/ 604 | 605 | #define LV_USE_SPAN 1 606 | #if LV_USE_SPAN 607 | /*A line text can contain maximum num of span descriptor */ 608 | #define LV_SPAN_SNIPPET_STACK_SIZE 64 609 | #endif 610 | 611 | #define LV_USE_SPINBOX 1 612 | 613 | #define LV_USE_SPINNER 1 614 | 615 | #define LV_USE_SWITCH 1 616 | 617 | #define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ 618 | #if LV_USE_TEXTAREA != 0 619 | #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ 620 | #endif 621 | 622 | #define LV_USE_TABLE 1 623 | 624 | #define LV_USE_TABVIEW 1 625 | 626 | #define LV_USE_TILEVIEW 1 627 | 628 | #define LV_USE_WIN 1 629 | 630 | /*================== 631 | * THEMES 632 | *==================*/ 633 | 634 | /*A simple, impressive and very complete theme*/ 635 | #define LV_USE_THEME_DEFAULT 1 636 | #if LV_USE_THEME_DEFAULT 637 | 638 | /*0: Light mode; 1: Dark mode*/ 639 | #define LV_THEME_DEFAULT_DARK 0 640 | 641 | /*1: Enable grow on press*/ 642 | #define LV_THEME_DEFAULT_GROW 1 643 | 644 | /*Default transition time in [ms]*/ 645 | #define LV_THEME_DEFAULT_TRANSITION_TIME 80 646 | #endif /*LV_USE_THEME_DEFAULT*/ 647 | 648 | /*A very simple theme that is a good starting point for a custom theme*/ 649 | #define LV_USE_THEME_SIMPLE 0 650 | 651 | /*A theme designed for monochrome displays*/ 652 | #define LV_USE_THEME_MONO 0 653 | 654 | /*================== 655 | * LAYOUTS 656 | *==================*/ 657 | 658 | /*A layout similar to Flexbox in CSS.*/ 659 | #define LV_USE_FLEX 1 660 | 661 | /*A layout similar to Grid in CSS.*/ 662 | #define LV_USE_GRID 1 663 | 664 | /*==================== 665 | * 3RD PARTS LIBRARIES 666 | *====================*/ 667 | 668 | /*File system interfaces for common APIs */ 669 | 670 | /*API for fopen, fread, etc*/ 671 | #define LV_USE_FS_STDIO 0 672 | #if LV_USE_FS_STDIO 673 | #define LV_FS_STDIO_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 674 | #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ 675 | #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ 676 | #endif 677 | 678 | /*API for open, read, etc*/ 679 | #define LV_USE_FS_POSIX 0 680 | #if LV_USE_FS_POSIX 681 | #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 682 | #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ 683 | #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ 684 | #endif 685 | 686 | /*API for CreateFile, ReadFile, etc*/ 687 | #define LV_USE_FS_WIN32 0 688 | #if LV_USE_FS_WIN32 689 | #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 690 | #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ 691 | #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ 692 | #endif 693 | 694 | /*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ 695 | #define LV_USE_FS_FATFS 0 696 | #if LV_USE_FS_FATFS 697 | #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 698 | #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ 699 | #endif 700 | 701 | /*API for memory-mapped file access. */ 702 | #define LV_USE_FS_MEMFS 1 703 | #if LV_USE_FS_MEMFS 704 | #define LV_FS_MEMFS_LETTER 'M' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 705 | #endif 706 | 707 | /*API for LittleFs. */ 708 | #define LV_USE_FS_LITTLEFS 0 709 | #if LV_USE_FS_LITTLEFS 710 | #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 711 | #endif 712 | 713 | /*API for Arduino LittleFs. */ 714 | #define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0 715 | #if LV_USE_FS_ARDUINO_ESP_LITTLEFS 716 | #define LV_FS_ARDUINO_ESP_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ 717 | #endif 718 | 719 | /*LODEPNG decoder library*/ 720 | #define LV_USE_LODEPNG 1 721 | 722 | /*PNG decoder(libpng) library*/ 723 | #define LV_USE_LIBPNG 0 724 | 725 | /*BMP decoder library*/ 726 | #define LV_USE_BMP 0 727 | 728 | /* JPG + split JPG decoder library. 729 | * Split JPG is a custom format optimized for embedded systems. */ 730 | #define LV_USE_TJPGD 1 731 | 732 | /* libjpeg-turbo decoder library. 733 | * Supports complete JPEG specifications and high-performance JPEG decoding. */ 734 | #define LV_USE_LIBJPEG_TURBO 0 735 | 736 | /*GIF decoder library*/ 737 | #define LV_USE_GIF 1 738 | #if LV_USE_GIF 739 | /*GIF decoder accelerate*/ 740 | #define LV_GIF_CACHE_DECODE_DATA 0 741 | #endif 742 | 743 | /*Decode bin images to RAM*/ 744 | #define LV_BIN_DECODER_RAM_LOAD 0 745 | 746 | /*RLE decompress library*/ 747 | #define LV_USE_RLE 0 748 | 749 | /*QR code library*/ 750 | #define LV_USE_QRCODE 1 751 | 752 | /*Barcode code library*/ 753 | #define LV_USE_BARCODE 1 754 | 755 | /*FreeType library*/ 756 | #ifdef MICROPY_FREETYPE 757 | #define LV_USE_FREETYPE MICROPY_FREETYPE 758 | #else 759 | #define LV_USE_FREETYPE 0 760 | #endif 761 | #if LV_USE_FREETYPE 762 | /*Let FreeType to use LVGL memory and file porting*/ 763 | #define LV_FREETYPE_USE_LVGL_PORT 0 764 | 765 | /*Cache count of the glyphs in FreeType. It means the number of glyphs that can be cached. 766 | *The higher the value, the more memory will be used.*/ 767 | #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256 768 | #endif 769 | 770 | /* Built-in TTF decoder */ 771 | #ifndef LV_USE_TINY_TTF 772 | #define LV_USE_TINY_TTF 0 773 | #endif 774 | #if LV_USE_TINY_TTF 775 | /* Enable loading TTF data from files */ 776 | #define LV_TINY_TTF_FILE_SUPPORT 0 777 | #endif 778 | 779 | /*Rlottie library*/ 780 | #ifdef MICROPY_RLOTTIE 781 | #define LV_USE_RLOTTIE MICROPY_RLOTTIE 782 | #else 783 | #define LV_USE_RLOTTIE 0 784 | #endif 785 | 786 | /*Enable Vector Graphic APIs*/ 787 | #define LV_USE_VECTOR_GRAPHIC 0 788 | 789 | /* Enable ThorVG (vector graphics library) from the src/libs folder */ 790 | #define LV_USE_THORVG_INTERNAL 0 791 | 792 | /* Enable ThorVG by assuming that its installed and linked to the project */ 793 | #define LV_USE_THORVG_EXTERNAL 0 794 | 795 | /*Use lvgl built-in LZ4 lib*/ 796 | #define LV_USE_LZ4_INTERNAL 0 797 | 798 | /*Use external LZ4 library*/ 799 | #define LV_USE_LZ4_EXTERNAL 0 800 | 801 | /*FFmpeg library for image decoding and playing videos 802 | *Supports all major image formats so do not enable other image decoder with it*/ 803 | #ifdef MICROPY_FFMPEG 804 | #define LV_USE_FFMPEG MICROPY_FFMPEG 805 | #else 806 | #define LV_USE_FFMPEG 0 807 | #endif 808 | #if LV_USE_FFMPEG 809 | /*Dump input information to stderr*/ 810 | #define LV_FFMPEG_DUMP_FORMAT 0 811 | #endif 812 | 813 | /*================== 814 | * OTHERS 815 | *==================*/ 816 | 817 | /*1: Enable API to take snapshot for object*/ 818 | #define LV_USE_SNAPSHOT 1 819 | 820 | /*1: Enable system monitor component*/ 821 | #define LV_USE_SYSMON 0 822 | #if LV_USE_SYSMON 823 | /*Get the idle percentage. E.g. uint32_t my_get_idle(void);*/ 824 | #define LV_SYSMON_GET_IDLE lv_timer_get_idle 825 | 826 | /*1: Show CPU usage and FPS count 827 | * Requires `LV_USE_SYSMON = 1`*/ 828 | #define LV_USE_PERF_MONITOR 0 829 | #if LV_USE_PERF_MONITOR 830 | #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT 831 | 832 | /*0: Displays performance data on the screen, 1: Prints performance data using log.*/ 833 | #define LV_USE_PERF_MONITOR_LOG_MODE 0 834 | #endif 835 | 836 | /*1: Show the used memory and the memory fragmentation 837 | * Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` 838 | * Requires `LV_USE_SYSMON = 1`*/ 839 | #define LV_USE_MEM_MONITOR 0 840 | #if LV_USE_MEM_MONITOR 841 | #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT 842 | #endif 843 | 844 | #endif /*LV_USE_SYSMON*/ 845 | 846 | /*1: Enable the runtime performance profiler*/ 847 | #define LV_USE_PROFILER 0 848 | #if LV_USE_PROFILER 849 | /*1: Enable the built-in profiler*/ 850 | #define LV_USE_PROFILER_BUILTIN 1 851 | #if LV_USE_PROFILER_BUILTIN 852 | /*Default profiler trace buffer size*/ 853 | #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024) /*[bytes]*/ 854 | #endif 855 | 856 | /*Header to include for the profiler*/ 857 | #define LV_PROFILER_INCLUDE "lvgl/src/misc/lv_profiler_builtin.h" 858 | 859 | /*Profiler start point function*/ 860 | #define LV_PROFILER_BEGIN LV_PROFILER_BUILTIN_BEGIN 861 | 862 | /*Profiler end point function*/ 863 | #define LV_PROFILER_END LV_PROFILER_BUILTIN_END 864 | 865 | /*Profiler start point function with custom tag*/ 866 | #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG 867 | 868 | /*Profiler end point function with custom tag*/ 869 | #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG 870 | #endif 871 | 872 | /*1: Enable Monkey test*/ 873 | #define LV_USE_MONKEY 0 874 | 875 | /*1: Enable grid navigation*/ 876 | #ifndef LV_USE_GRIDNAV 877 | #define LV_USE_GRIDNAV 0 878 | #endif 879 | 880 | /*1: Enable lv_obj fragment*/ 881 | #define LV_USE_FRAGMENT 0 882 | 883 | /*1: Support using images as font in label or span widgets */ 884 | #define LV_USE_IMGFONT 1 885 | 886 | /*1: Enable an observer pattern implementation*/ 887 | #define LV_USE_OBSERVER 1 888 | 889 | /*1: Enable Pinyin input method*/ 890 | /*Requires: lv_keyboard*/ 891 | #ifndef LV_USE_IME_PINYIN 892 | #define LV_USE_IME_PINYIN 0 893 | #endif 894 | #if LV_USE_IME_PINYIN 895 | /*1: Use default thesaurus*/ 896 | /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ 897 | #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 898 | /*Set the maximum number of candidate panels that can be displayed*/ 899 | /*This needs to be adjusted according to the size of the screen*/ 900 | #define LV_IME_PINYIN_CAND_TEXT_NUM 6 901 | 902 | /*Use 9 key input(k9)*/ 903 | #define LV_IME_PINYIN_USE_K9_MODE 1 904 | #if LV_IME_PINYIN_USE_K9_MODE == 1 905 | #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 906 | #endif /*LV_IME_PINYIN_USE_K9_MODE*/ 907 | #endif 908 | 909 | /*1: Enable file explorer*/ 910 | /*Requires: lv_table*/ 911 | #ifndef LV_USE_FILE_EXPLORER 912 | #define LV_USE_FILE_EXPLORER 0 913 | #endif 914 | #if LV_USE_FILE_EXPLORER 915 | /*Maximum length of path*/ 916 | #define LV_FILE_EXPLORER_PATH_MAX_LEN (128) 917 | /*Quick access bar, 1:use, 0:not use*/ 918 | /*Requires: lv_list*/ 919 | #define LV_FILE_EXPLORER_QUICK_ACCESS 1 920 | #endif 921 | 922 | /*================== 923 | * DEVICES 924 | *==================*/ 925 | 926 | /*Use SDL to open window on PC and handle mouse and keyboard*/ 927 | #ifdef MICROPY_SDL 928 | #define LV_USE_SDL MICROPY_SDL 929 | #else 930 | #define LV_USE_SDL 0 931 | #endif 932 | #if LV_USE_SDL 933 | #define LV_SDL_INCLUDE_PATH 934 | #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /*LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance*/ 935 | #define LV_SDL_BUF_COUNT 1 /*1 or 2*/ 936 | #define LV_SDL_FULLSCREEN 0 /*1: Make the window full screen by default*/ 937 | #define LV_SDL_DIRECT_EXIT 1 /*1: Exit the application when all SDL windows are closed*/ 938 | #define LV_SDL_MOUSEWHEEL_MODE LV_SDL_MOUSEWHEEL_MODE_ENCODER /*LV_SDL_MOUSEWHEEL_MODE_ENCODER/CROWN*/ 939 | #endif 940 | 941 | /*Use X11 to open window on Linux desktop and handle mouse and keyboard*/ 942 | #define LV_USE_X11 0 943 | #if LV_USE_X11 944 | #define LV_X11_DIRECT_EXIT 1 /*Exit the application when all X11 windows have been closed*/ 945 | #define LV_X11_DOUBLE_BUFFER 1 /*Use double buffers for endering*/ 946 | /*select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!)*/ 947 | #define LV_X11_RENDER_MODE_PARTIAL 1 /*Partial render mode (preferred)*/ 948 | #define LV_X11_RENDER_MODE_DIRECT 0 /*direct render mode*/ 949 | #define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/ 950 | #endif 951 | 952 | /*Driver for /dev/fb*/ 953 | #ifdef MICROPY_FB 954 | #define LV_USE_LINUX_FBDEV MICROPY_FB 955 | #else 956 | #define LV_USE_LINUX_FBDEV 0 957 | #endif 958 | #if LV_USE_LINUX_FBDEV 959 | #define LV_LINUX_FBDEV_BSD 0 960 | #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL 961 | #define LV_LINUX_FBDEV_BUFFER_COUNT 0 962 | #define LV_LINUX_FBDEV_BUFFER_SIZE 60 963 | #endif 964 | 965 | /*Use Nuttx to open window and handle touchscreen*/ 966 | #define LV_USE_NUTTX 0 967 | #if LV_USE_NUTTX 968 | #define LV_USE_NUTTX_LIBUV 0 969 | 970 | /*Use Nuttx custom init API to open window and handle touchscreen*/ 971 | #define LV_USE_NUTTX_CUSTOM_INIT 0 972 | 973 | /*Driver for /dev/lcd*/ 974 | #define LV_USE_NUTTX_LCD 0 975 | #if LV_USE_NUTTX_LCD 976 | #define LV_NUTTX_LCD_BUFFER_COUNT 0 977 | #define LV_NUTTX_LCD_BUFFER_SIZE 60 978 | #endif 979 | 980 | /*Driver for /dev/input*/ 981 | #define LV_USE_NUTTX_TOUCHSCREEN 0 982 | 983 | #endif 984 | 985 | /*Driver for /dev/dri/card*/ 986 | #define LV_USE_LINUX_DRM 0 987 | 988 | /*Interface for TFT_eSPI*/ 989 | #define LV_USE_TFT_ESPI 0 990 | 991 | /*Driver for evdev input devices*/ 992 | #define LV_USE_EVDEV 0 993 | 994 | /*Driver for libinput input devices*/ 995 | #define LV_USE_LIBINPUT 0 996 | #if LV_USE_LIBINPUT 997 | #define LV_LIBINPUT_BSD 0 998 | 999 | /*Full keyboard support*/ 1000 | #define LV_LIBINPUT_XKB 0 1001 | #if LV_LIBINPUT_XKB 1002 | /*"setxkbmap -query" can help find the right values for your keyboard*/ 1003 | #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL } 1004 | #endif 1005 | #endif 1006 | 1007 | /*Drivers for LCD devices connected via SPI/parallel port*/ 1008 | #define LV_USE_ST7735 0 1009 | #define LV_USE_ST7789 0 1010 | #define LV_USE_ST7796 0 1011 | #define LV_USE_ILI9341 0 1012 | 1013 | #define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) 1014 | 1015 | /* LVGL Windows backend */ 1016 | #define LV_USE_WINDOWS 0 1017 | 1018 | /*================== 1019 | * EXAMPLES 1020 | *==================*/ 1021 | 1022 | /*Enable the examples to be built with the library*/ 1023 | #ifndef LV_BUILD_EXAMPLES 1024 | #define LV_BUILD_EXAMPLES 0 1025 | #endif 1026 | 1027 | /*=================== 1028 | * DEMO USAGE 1029 | ====================*/ 1030 | 1031 | /*Show some widget. It might be required to increase `LV_MEM_SIZE` */ 1032 | #define LV_USE_DEMO_WIDGETS 0 1033 | 1034 | /*Demonstrate the usage of encoder and keyboard*/ 1035 | #define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 1036 | 1037 | /*Benchmark your system*/ 1038 | #define LV_USE_DEMO_BENCHMARK 0 1039 | 1040 | /*Render test for each primitives. Requires at least 480x272 display*/ 1041 | #define LV_USE_DEMO_RENDER 0 1042 | 1043 | /*Stress test for LVGL*/ 1044 | #define LV_USE_DEMO_STRESS 0 1045 | 1046 | /*Music player demo*/ 1047 | #define LV_USE_DEMO_MUSIC 0 1048 | #if LV_USE_DEMO_MUSIC 1049 | #define LV_DEMO_MUSIC_SQUARE 0 1050 | #define LV_DEMO_MUSIC_LANDSCAPE 0 1051 | #define LV_DEMO_MUSIC_ROUND 0 1052 | #define LV_DEMO_MUSIC_LARGE 0 1053 | #define LV_DEMO_MUSIC_AUTO_PLAY 0 1054 | #endif 1055 | 1056 | /*Flex layout demo*/ 1057 | #define LV_USE_DEMO_FLEX_LAYOUT 0 1058 | 1059 | /*Smart-phone like multi-language demo*/ 1060 | #define LV_USE_DEMO_MULTILANG 0 1061 | 1062 | /*Widget transformation demo*/ 1063 | #define LV_USE_DEMO_TRANSFORM 0 1064 | 1065 | /*Demonstrate scroll settings*/ 1066 | #define LV_USE_DEMO_SCROLL 0 1067 | 1068 | /*Vector graphic demo*/ 1069 | #define LV_USE_DEMO_VECTOR_GRAPHIC 0 1070 | /*--END OF LV_CONF_H--*/ 1071 | 1072 | #endif /*LV_CONF_H*/ 1073 | 1074 | #endif /*End of "Content enable"*/ 1075 | -------------------------------------------------------------------------------- /binding/gen/gen_mpy.py: -------------------------------------------------------------------------------- 1 | # TODO 2 | # - Array conversion improvements: 3 | # - return custom iterable object instead of Blob when converting to array 4 | # - check array dim on conversion 5 | # - On print extensions, print the reflected internal representation of the object (worth the extra ROM?) 6 | # - Verify that when mp_obj is given it is indeed the right type (mp_lv_obj_t). Report error if not. can be added to mp_to_lv. 7 | # - Implement inheritance instead of embed base methods (how? seems it's not supported, see https://github.com/micropython/micropython/issues/1159) 8 | # - When converting mp to ptr (and vice versa), verify that types are compatible. Now all pointers are casted to void*. 9 | 10 | from __future__ import print_function 11 | import collections 12 | import sys 13 | import copy 14 | from functools import lru_cache 15 | import json 16 | import os 17 | 18 | 19 | def memoize(func): 20 | @lru_cache(maxsize=1000000) 21 | def memoized(*args, **kwargs): 22 | return func(*args, **kwargs) 23 | 24 | return memoized 25 | 26 | 27 | def eprint(*args, **kwargs): 28 | print(*args, file=sys.stderr, **kwargs) 29 | 30 | 31 | # from pudb.remote import set_trace 32 | # set_trace(term_size=(180, 50)) 33 | 34 | from sys import argv 35 | from argparse import ArgumentParser 36 | import subprocess 37 | import re 38 | from os.path import dirname, abspath 39 | from os.path import commonprefix 40 | 41 | script_path = dirname(abspath(__file__)) 42 | sys.path.insert(0, "%s/../pycparser" % script_path) 43 | from pycparser import c_parser, c_ast, c_generator 44 | 45 | # 46 | # Argument parsing 47 | # 48 | 49 | argParser = ArgumentParser() 50 | argParser.add_argument( 51 | "-I", 52 | "--include", 53 | dest="include", 54 | help="Preprocesor include path", 55 | metavar="", 56 | action="append", 57 | ) 58 | argParser.add_argument( 59 | "-D", 60 | "--define", 61 | dest="define", 62 | help="Define preprocessor macro", 63 | metavar="", 64 | action="append", 65 | ) 66 | argParser.add_argument( 67 | "-E", 68 | "--external-preprocessing", 69 | dest="ep", 70 | help="Prevent preprocessing. Assume input file is already preprocessed", 71 | metavar="", 72 | action="store", 73 | ) 74 | argParser.add_argument( 75 | "-M", 76 | "--module_name", 77 | dest="module_name", 78 | help="Module name", 79 | metavar="", 80 | action="store", 81 | ) 82 | argParser.add_argument( 83 | "-MP", 84 | "--module_prefix", 85 | dest="module_prefix", 86 | help="Module prefix that starts every function name", 87 | metavar="", 88 | action="store", 89 | ) 90 | argParser.add_argument( 91 | "-MD", 92 | "--metadata", 93 | dest="metadata", 94 | help="Optional file to emit metadata (introspection)", 95 | metavar="", 96 | action="store", 97 | ) 98 | argParser.add_argument("input", nargs="+") 99 | argParser.set_defaults(include=[], define=[], ep=None, input=[]) 100 | args = argParser.parse_args() 101 | 102 | module_name = args.module_name 103 | module_prefix = args.module_prefix if args.module_prefix else args.module_name 104 | 105 | # 106 | # C proceprocessing, if needed, or just read the input files. 107 | # 108 | 109 | if not args.ep: 110 | pp_cmd = ( 111 | "gcc -E -std=c99 -DPYCPARSER {macros} {include} {input} {first_input}".format( 112 | input=" ".join("-include %s" % inp for inp in args.input), 113 | first_input="%s" % args.input[0], 114 | macros=" ".join("-D%s" % define for define in args.define), 115 | include=" ".join("-I %s" % inc for inc in args.include), 116 | ) 117 | ) 118 | s = subprocess.check_output(pp_cmd.split()).decode() 119 | else: 120 | pp_cmd = "Preprocessing was disabled." 121 | s = "" 122 | with open(args.ep, "r") as f: 123 | s += f.read() 124 | # 125 | # AST parsing helper functions 126 | # 127 | 128 | 129 | @memoize 130 | def remove_declname(ast): 131 | if hasattr(ast, "declname"): 132 | ast.declname = None 133 | if isinstance(ast, tuple): 134 | remove_declname(ast[1]) 135 | return 136 | for i, c1 in enumerate(ast.children()): 137 | child = ast.children()[i] 138 | remove_declname(child) 139 | 140 | 141 | @memoize 142 | def add_default_declname(ast, name): 143 | if hasattr(ast, "declname"): 144 | if ast.declname == None: 145 | ast.declname = name 146 | if isinstance(ast, tuple): 147 | add_default_declname(ast[1], name) 148 | return 149 | for i, c1 in enumerate(ast.children()): 150 | child = ast.children()[i] 151 | add_default_declname(child, name) 152 | 153 | 154 | @memoize 155 | def convert_array_to_ptr(ast): 156 | if hasattr(ast, "type") and isinstance(ast.type, c_ast.ArrayDecl): 157 | ast.type = c_ast.PtrDecl( 158 | ast.type.quals if hasattr(ast.type, "quals") else [], ast.type.type 159 | ) 160 | if isinstance(ast, tuple): 161 | return convert_array_to_ptr(ast[1]) 162 | for i, c1 in enumerate(ast.children()): 163 | child = ast.children()[i] 164 | convert_array_to_ptr(child) 165 | 166 | 167 | @memoize 168 | def remove_quals(ast): 169 | if hasattr(ast, "quals"): 170 | ast.quals = [] 171 | if hasattr(ast, "dim_quals"): 172 | ast.dim_quals = [] 173 | if isinstance(ast, tuple): 174 | return remove_quals(ast[1]) 175 | for i, c1 in enumerate(ast.children()): 176 | child = ast.children()[i] 177 | if not isinstance( 178 | child, c_ast.FuncDecl 179 | ): # Don't remove quals which change function prototype 180 | remove_quals(child) 181 | 182 | 183 | @memoize 184 | def remove_explicit_struct(ast): 185 | if isinstance(ast, c_ast.TypeDecl) and isinstance(ast.type, c_ast.Struct): 186 | explicit_struct_name = ast.type.name 187 | # eprint('--> replace %s by %s in:\n%s' % (explicit_struct_name, explicit_structs[explicit_struct_name] if explicit_struct_name in explicit_structs else '???', ast)) 188 | if explicit_struct_name: 189 | if explicit_struct_name in explicit_structs: 190 | ast.type = c_ast.IdentifierType( 191 | [explicit_structs[explicit_struct_name]] 192 | ) 193 | elif explicit_struct_name in structs: 194 | ast.type = c_ast.IdentifierType([explicit_struct_name]) 195 | if isinstance(ast, tuple): 196 | return remove_explicit_struct(ast[1]) 197 | for i, c1 in enumerate(ast.children()): 198 | child = ast.children()[i] 199 | remove_explicit_struct(child) 200 | 201 | 202 | @memoize 203 | def get_type(arg, **kwargs): 204 | if isinstance(arg, str): 205 | return arg 206 | remove_quals_arg = "remove_quals" in kwargs and kwargs["remove_quals"] 207 | arg_ast = copy.deepcopy(arg) 208 | remove_explicit_struct(arg_ast) 209 | if remove_quals_arg: 210 | remove_quals(arg_ast) 211 | return gen.visit(arg_ast) 212 | 213 | 214 | @memoize 215 | def get_name(type): 216 | if isinstance(type, c_ast.Decl): 217 | return type.name 218 | if isinstance(type, c_ast.Struct) and type.name and type.name in explicit_structs: 219 | return explicit_structs[type.name] 220 | if isinstance(type, c_ast.Struct): 221 | return type.name 222 | if isinstance(type, c_ast.TypeDecl): 223 | return type.declname 224 | if isinstance(type, c_ast.IdentifierType): 225 | return type.names[0] 226 | if isinstance(type, c_ast.FuncDecl): 227 | return type.type.declname 228 | # if isinstance(type, (c_ast.PtrDecl, c_ast.ArrayDecl)) and hasattr(type.type, 'declname'): 229 | # return type.type.declname 230 | if isinstance(type, (c_ast.PtrDecl, c_ast.ArrayDecl)): 231 | return get_type(type, remove_quals=True) 232 | else: 233 | return gen.visit(type) 234 | 235 | 236 | @memoize 237 | def remove_arg_names(ast): 238 | if isinstance(ast, c_ast.TypeDecl): 239 | ast.declname = None 240 | remove_arg_names(ast.type) 241 | elif isinstance(ast, c_ast.Decl): 242 | remove_arg_names(ast.type) 243 | elif isinstance(ast, c_ast.FuncDecl): 244 | remove_arg_names(ast.args) 245 | elif isinstance(ast, c_ast.ParamList): 246 | for param in ast.params: 247 | remove_arg_names(param) 248 | 249 | 250 | # Create a function prototype AST from a function AST 251 | @memoize 252 | def function_prototype(func): 253 | bare_func = copy.deepcopy(func) 254 | remove_declname(bare_func) 255 | 256 | ptr_decl = c_ast.PtrDecl(quals=[], type=bare_func.type) 257 | 258 | func_proto = c_ast.Typename(name=None, quals=[], align=[], type=ptr_decl) 259 | 260 | return func_proto 261 | 262 | 263 | # 264 | # module specific text patterns 265 | # IGNORECASE and "lower" are used to match both function and enum names 266 | # 267 | 268 | base_obj_name = "obj" 269 | base_obj_type = "%s_%s_t" % (module_prefix, base_obj_name) 270 | lv_ext_pattern = re.compile("^{prefix}_([^_]+)_ext_t".format(prefix=module_prefix)) 271 | lv_obj_pattern = re.compile( 272 | "^{prefix}_([^_]+)".format(prefix=module_prefix), re.IGNORECASE 273 | ) 274 | lv_func_pattern = re.compile( 275 | "^{prefix}_(.+)".format(prefix=module_prefix), re.IGNORECASE 276 | ) 277 | create_obj_pattern = re.compile("^{prefix}_(.+)_create$".format(prefix=module_prefix)) 278 | lv_method_pattern = re.compile( 279 | "^{prefix}_[^_]+_(.+)".format(prefix=module_prefix), re.IGNORECASE 280 | ) 281 | lv_base_obj_pattern = re.compile( 282 | "^(struct _){{0,1}}{prefix}_{base_name}_t( [*]){{0,1}}".format( 283 | prefix=module_prefix, base_name=base_obj_name 284 | ) 285 | ) 286 | lv_str_enum_pattern = re.compile( 287 | "^_{prefix}_STR_(.+)".format(prefix=module_prefix.upper()) 288 | ) 289 | lv_callback_type_pattern = re.compile( 290 | "({prefix}_){{0,1}}(.+)_cb(_t){{0,1}}".format(prefix=module_prefix) 291 | ) 292 | lv_global_callback_pattern = re.compile(".*g_cb_t") 293 | lv_func_returns_array = re.compile(".*_array$") 294 | lv_enum_name_pattern = re.compile( 295 | "^(ENUM_){{0,1}}({prefix}_){{0,1}}(.*)".format(prefix=module_prefix.upper()) 296 | ) 297 | 298 | 299 | # Prevent identifier names which are Python reserved words (add underscore in such case) 300 | def sanitize( 301 | id, 302 | kwlist=[ 303 | "False", 304 | "None", 305 | "True", 306 | "and", 307 | "as", 308 | "assert", 309 | "break", 310 | "class", 311 | "continue", 312 | "def", 313 | "del", 314 | "elif", 315 | "else", 316 | "except", 317 | "finally", 318 | "for", 319 | "from", 320 | "global", 321 | "if", 322 | "import", 323 | "in", 324 | "is", 325 | "lambda", 326 | "nonlocal", 327 | "not", 328 | "or", 329 | "pass", 330 | "raise", 331 | "return", 332 | "try", 333 | "while", 334 | "with", 335 | "yield", 336 | ], 337 | ): 338 | if id in kwlist: 339 | result = "_%s" % id 340 | else: 341 | result = id 342 | result = result.strip() 343 | result = result.replace(" ", "_") 344 | result = result.replace("*", "_ptr") 345 | return result 346 | 347 | 348 | @memoize 349 | def simplify_identifier(id): 350 | match_result = lv_func_pattern.match(id) 351 | return match_result.group(1) if match_result else id 352 | 353 | 354 | def obj_name_from_ext_name(ext_name): 355 | return lv_ext_pattern.match(ext_name).group(1) 356 | 357 | 358 | def obj_name_from_func_name(func_name): 359 | return lv_obj_pattern.match(func_name).group(1) 360 | 361 | 362 | def ctor_name_from_obj_name(obj_name): 363 | return "{prefix}_{obj}_create".format(prefix=module_prefix, obj=obj_name) 364 | 365 | 366 | def is_method_of(func_name, obj_name): 367 | return func_name.lower().startswith( 368 | "{prefix}_{obj}_".format(prefix=module_prefix, obj=obj_name).lower() 369 | ) 370 | 371 | 372 | def method_name_from_func_name(func_name): 373 | res = lv_method_pattern.match(func_name).group(1) 374 | return res if res != "del" else "delete" # del is a reserved name, don't use it 375 | 376 | 377 | def get_enum_name(enum): 378 | match_result = lv_enum_name_pattern.match(enum) 379 | return match_result.group(3) if match_result else enum 380 | 381 | 382 | def str_enum_to_str(str_enum): 383 | res = lv_str_enum_pattern.match(str_enum).group(1) 384 | return ("%s_" % module_prefix.upper()) + res 385 | 386 | 387 | def is_obj_ctor(func): 388 | # ctor name must match pattern 389 | if not create_obj_pattern.match(func.name): 390 | return False 391 | # ctor must return a base_obj type 392 | if not lv_base_obj_pattern.match(get_type(func.type.type, remove_quals=True)): 393 | return False 394 | # ctor must receive (at least) one base obj parameters 395 | args = func.type.args.params 396 | if len(args) < 1: 397 | return False 398 | if not lv_base_obj_pattern.match(get_type(args[0].type, remove_quals=True)): 399 | return False 400 | return True 401 | 402 | 403 | def is_global_callback(arg_type): 404 | arg_type_str = get_name(arg_type.type) 405 | # print('/* --> is_global_callback %s: %s */' % (lv_global_callback_pattern.match(arg_type_str), arg_type_str)) 406 | result = lv_global_callback_pattern.match(arg_type_str) 407 | return result 408 | 409 | 410 | # 411 | # Initialization, data structures, helper functions 412 | # 413 | 414 | 415 | # We consider union as a struct, for simplicity 416 | def is_struct(type): 417 | return isinstance(type, c_ast.Struct) or isinstance(type, c_ast.Union) 418 | 419 | 420 | obj_metadata = collections.OrderedDict() 421 | func_metadata = collections.OrderedDict() 422 | callback_metadata = collections.OrderedDict() 423 | 424 | func_prototypes = {} 425 | 426 | parser = c_parser.CParser() 427 | gen = c_generator.CGenerator() 428 | ast = parser.parse(s, filename="") 429 | 430 | 431 | # *************** Fix *********************************** 432 | # this is a fix for structures not getting populated properly from 433 | # forward declarations. pycparser doesn't make the connection between 434 | # a forwrd declaration and the actual declaration so the structures get created 435 | # without any fields. Since there are no fields things like callbacks break 436 | # because the generation code looks for specific field names 437 | # to know if it is valid to be used as a callback. 438 | forward_struct_decls = {} 439 | 440 | for item in ast.ext[:]: 441 | # Locate a forward declaration 442 | if ( 443 | isinstance(item, c_ast.Decl) 444 | and item.name is None 445 | and isinstance(item.type, c_ast.Struct) 446 | and item.type.name is not None 447 | ): 448 | # check to see if there are no fields , and of not store the structure 449 | # as a foward declaration. If it does have fields then build a single 450 | # object that represents the structure with the fiels. 451 | if item.type.decls is None: 452 | forward_struct_decls[item.type.name] = [item] 453 | else: 454 | if item.type.name in forward_struct_decls: 455 | decs = forward_struct_decls[item.type.name] 456 | if len(decs) == 2: 457 | decl, td = decs 458 | 459 | td.type.type.decls = item.type.decls[:] 460 | 461 | ast.ext.remove(decl) 462 | ast.ext.remove(item) 463 | # there are 3 objects that get created for a formard declaration. 464 | # a structure without any fields, a typedef pointing to that structure. 465 | # and the last is another structure that has fields. So we need to capture 466 | # all 3 parts to build a single object that represents a structure. 467 | elif ( 468 | isinstance(item, c_ast.Typedef) 469 | and isinstance(item.type, c_ast.TypeDecl) 470 | and item.name 471 | and item.type.declname 472 | and item.name == item.type.declname 473 | and isinstance(item.type.type, c_ast.Struct) 474 | and item.type.type.decls is None 475 | ): 476 | if item.type.type.name in forward_struct_decls: 477 | forward_struct_decls[item.type.type.name].append(item) 478 | 479 | # ******************************************************************** 480 | 481 | 482 | # Types and structs 483 | 484 | typedefs = [ 485 | x.type for x in ast.ext if isinstance(x, c_ast.Typedef) 486 | ] # and not (hasattr(x.type, 'declname') and lv_base_obj_pattern.match(x.type.declname))] 487 | # print('/* %s */' % str(typedefs)) 488 | synonym = {} 489 | for t in typedefs: 490 | if isinstance(t, c_ast.TypeDecl) and isinstance(t.type, c_ast.IdentifierType): 491 | if t.declname != t.type.names[0]: 492 | synonym[t.declname] = t.type.names[0] 493 | # eprint('%s === %s' % (t.declname, t.type.names[0])) 494 | if isinstance(t, c_ast.TypeDecl) and isinstance(t.type, c_ast.Struct): 495 | if t.declname != t.type.name: 496 | synonym[t.declname] = t.type.name 497 | # eprint('%s === struct %s' % (t.declname, t.type.name)) 498 | struct_typedefs = [typedef for typedef in typedefs if is_struct(typedef.type)] 499 | structs_without_typedef = collections.OrderedDict( 500 | (decl.type.name, decl.type) 501 | for decl in ast.ext 502 | if hasattr(decl, "type") and is_struct(decl.type) 503 | ) 504 | 505 | # for typedefs that referenced to a forward declaration struct, replace it with the real definition. 506 | for typedef in struct_typedefs: 507 | if typedef.type.decls is None: # None means it's a forward declaration 508 | struct_name = typedef.type.name 509 | # check if it's found in `structs_without_typedef`. It actually has the typedef. Replace type with it. 510 | if typedef.type.name in structs_without_typedef: 511 | typedef.type = structs_without_typedef[struct_name] 512 | 513 | structs = collections.OrderedDict( 514 | (typedef.declname, typedef.type) 515 | for typedef in struct_typedefs 516 | if typedef.declname and typedef.type.decls 517 | ) # and not lv_base_obj_pattern.match(typedef.declname)) 518 | structs.update(structs_without_typedef) # This is for struct without typedef 519 | explicit_structs = collections.OrderedDict( 520 | (typedef.type.name, typedef.declname) 521 | for typedef in struct_typedefs 522 | if typedef.type.name 523 | ) # and not lv_base_obj_pattern.match(typedef.type.name)) 524 | opaque_structs = collections.OrderedDict( 525 | (typedef.declname, c_ast.Struct(name=typedef.declname, decls=[])) 526 | for typedef in typedefs 527 | if isinstance(typedef.type, c_ast.Struct) and typedef.type.decls == None 528 | ) 529 | structs.update({k: v for k, v in opaque_structs.items() if k not in structs}) 530 | # print('/* --> opaque structs len = %d */' % len(opaque_structs)) 531 | # print('/* --> opaque structs %s */' % ',\n'.join([struct_name for struct_name in opaque_structs])) 532 | # print('/* --> structs:\n%s */' % ',\n'.join(sorted(str(structs[struct_name]) for struct_name in structs if struct_name))) 533 | # print('/* --> structs_without_typedef:\n%s */' % ',\n'.join(sorted(str(structs_without_typedef[struct_name]) for struct_name in structs_without_typedef if struct_name))) 534 | # print('/* --> explicit_structs:\n%s */' % ',\n'.join(sorted(struct_name + " = " + str(explicit_structs[struct_name]) for struct_name in explicit_structs if struct_name))) 535 | # print('/* --> structs without typedef:\n%s */' % ',\n'.join(sorted(str(structs[struct_name]) for struct_name in structs_without_typedef))) 536 | 537 | # Functions and objects 538 | 539 | func_defs = [x.decl for x in ast.ext if isinstance(x, c_ast.FuncDef)] 540 | func_decls = [ 541 | x 542 | for x in ast.ext 543 | if isinstance(x, c_ast.Decl) and isinstance(x.type, c_ast.FuncDecl) 544 | ] 545 | all_funcs = func_defs + func_decls 546 | funcs = [ 547 | f for f in all_funcs if not f.name.startswith("_") 548 | ] # functions that start with underscore are usually internal 549 | # eprint('... %s' % ',\n'.join(sorted('%s' % func.name for func in funcs))) 550 | obj_ctors = [func for func in funcs if is_obj_ctor(func)] 551 | # eprint('CTORS(%d): %s' % (len(obj_ctors), ', '.join(sorted('%s' % ctor.name for ctor in obj_ctors)))) 552 | for obj_ctor in obj_ctors: 553 | funcs.remove(obj_ctor) 554 | obj_names = [create_obj_pattern.match(ctor.name).group(1) for ctor in obj_ctors] 555 | 556 | 557 | def has_ctor(obj_name): 558 | return ctor_name_from_obj_name(obj_name) in [ctor.name for ctor in obj_ctors] 559 | 560 | 561 | def get_ctor(obj_name): 562 | global obj_ctors 563 | return next( 564 | ctor for ctor in obj_ctors if ctor.name == ctor_name_from_obj_name(obj_name) 565 | ) 566 | 567 | 568 | def get_methods(obj_name): 569 | global funcs 570 | return [ 571 | func 572 | for func in funcs 573 | if is_method_of(func.name, obj_name) 574 | and (not func.name == ctor_name_from_obj_name(obj_name)) 575 | ] 576 | 577 | 578 | @memoize 579 | def noncommon_part(member_name, stem_name): 580 | common_part = commonprefix([member_name, stem_name]) 581 | n = len(common_part) - 1 582 | while n > 0 and member_name[n] != "_": 583 | n -= 1 584 | return member_name[n + 1 :] 585 | 586 | 587 | @memoize 588 | def get_first_arg(func): 589 | if not func.type.args: 590 | return None 591 | if not len(func.type.args.params) >= 1: 592 | return None 593 | if not func.type.args.params[0].type: 594 | return None 595 | return func.type.args.params[0].type 596 | 597 | 598 | @memoize 599 | def get_first_arg_type(func): 600 | first_arg = get_first_arg(func) 601 | if not first_arg: 602 | return None 603 | if not first_arg.type: 604 | return None 605 | return get_type(first_arg.type, remove_quals=True) 606 | 607 | 608 | def get_base_struct_name(struct_name): 609 | return struct_name[:-2] if struct_name.endswith("_t") else struct_name 610 | 611 | 612 | # "struct function" starts with struct name (without _t), and their first argument is a pointer to the struct 613 | # Need also to take into account struct functions of aliases of current struct. 614 | @memoize 615 | def get_struct_functions(struct_name): 616 | global funcs 617 | if not struct_name: 618 | return [] 619 | base_struct_name = get_base_struct_name(struct_name) 620 | # eprint("get_struct_functions %s: %s" % (struct_name, [get_type(func.type.args.params[0].type.type, remove_quals = True) for func in funcs if func.name.startswith(base_struct_name)])) 621 | # eprint("get_struct_functions %s: %s" % (struct_name, struct_aliases[struct_name] if struct_name in struct_aliases else "")) 622 | 623 | # for func in funcs: 624 | # print("/* get_struct_functions: func=%s, struct=%s, noncommon part=%s */" % (simplify_identifier(func.name), simplify_identifier(struct_name), 625 | # noncommon_part(simplify_identifier(func.name), simplify_identifier(struct_name)))) 626 | 627 | reverse_aliases = [ 628 | alias for alias in struct_aliases if struct_aliases[alias] == struct_name 629 | ] 630 | 631 | return ( 632 | [ 633 | func 634 | for func in funcs 635 | if noncommon_part( 636 | simplify_identifier(func.name), simplify_identifier(struct_name) 637 | ) 638 | != simplify_identifier(func.name) 639 | and get_first_arg_type(func) == struct_name 640 | ] 641 | if (struct_name in structs or len(reverse_aliases) > 0) 642 | else [] 643 | ) + ( 644 | get_struct_functions(struct_aliases[struct_name]) 645 | if struct_name in struct_aliases 646 | else [] 647 | ) 648 | 649 | 650 | @memoize 651 | def is_struct_function(func): 652 | return func in get_struct_functions(get_first_arg_type(func)) 653 | 654 | 655 | # is_static_member returns true if function does not receive the obj as the first argument 656 | # and the object is not a struct function 657 | 658 | 659 | @memoize 660 | def is_static_member(func, obj_type=base_obj_type): 661 | first_arg = get_first_arg(func) 662 | # print("/* %s : get_first_arg = %s */" % (get_name(func), first_arg)) 663 | if first_arg: 664 | if isinstance(first_arg, c_ast.ArrayDecl): 665 | return True # Arrays cannot have non static members 666 | if is_struct_function(func): 667 | return False 668 | first_arg_type = get_first_arg_type(func) 669 | return (first_arg_type == None) or (first_arg_type != obj_type) 670 | 671 | 672 | # All object should inherit directly from base_obj, and not according to lv_ext, as disccussed on https://github.com/littlevgl/lv_binding_micropython/issues/19 673 | parent_obj_names = { 674 | child_name: base_obj_name for child_name in obj_names if child_name != base_obj_name 675 | } 676 | parent_obj_names[base_obj_name] = None 677 | 678 | # Populate inheritance hierarchy according to lv_ext structures 679 | # exts = {obj_name_from_ext_name(ext.name): ext for ext in ast.ext if hasattr(ext, 'name') and ext.name is not None and lv_ext_pattern.match(ext.name)} 680 | # for obj_name, ext in exts.items(): 681 | # try: 682 | # parent_ext_name = ext.type.type.decls[0].type.type.names[0] 683 | # if lv_ext_pattern.match(parent_ext_name): 684 | # parent_obj_names[obj_name] = obj_name_from_ext_name(parent_ext_name) 685 | # except AttributeError: 686 | # pass 687 | 688 | # Parse Enums 689 | 690 | enum_defs = [ 691 | x for x in ast.ext if hasattr(x, "type") and isinstance(x.type, c_ast.Enum) 692 | ] 693 | enum_defs += [ 694 | x.type 695 | for x in ast.ext 696 | if hasattr(x, "type") 697 | and hasattr(x.type, "type") 698 | and isinstance(x.type, c_ast.TypeDecl) 699 | and isinstance(x.type.type, c_ast.Enum) 700 | ] 701 | 702 | # Enum member access functions. 703 | 704 | 705 | def get_enum_members(obj_name): 706 | global enums 707 | if obj_name not in enums: 708 | return [] 709 | return [enum_member_name for enum_member_name, value in enums[obj_name].items()] 710 | 711 | 712 | def get_enum_member_name(enum_member): 713 | if enum_member[0].isdigit(): 714 | enum_member = "_" + enum_member # needs to be a valid attribute name 715 | return enum_member 716 | 717 | 718 | def get_enum_value(obj_name, enum_member): 719 | return enums[obj_name][enum_member] 720 | 721 | 722 | # eprint(enums) 723 | 724 | # parse function pointers 725 | func_typedefs = collections.OrderedDict( 726 | (t.name, t) 727 | for t in ast.ext 728 | if isinstance(t, c_ast.Typedef) 729 | and isinstance(t.type, c_ast.PtrDecl) 730 | and isinstance(t.type.type, c_ast.FuncDecl) 731 | ) 732 | 733 | # Global blobs 734 | blobs = collections.OrderedDict( 735 | (decl.name, decl.type.type) 736 | for decl in ast.ext 737 | if isinstance(decl, c_ast.Decl) 738 | and "extern" in decl.storage 739 | and hasattr(decl, "type") 740 | and isinstance(decl.type, c_ast.TypeDecl) 741 | and not decl.name.startswith("_") 742 | ) 743 | 744 | blobs["_nesting"] = parser.parse("extern int _nesting;").ext[0].type.type 745 | 746 | int_constants = [] 747 | 748 | # 749 | # Type convertors 750 | # 751 | 752 | 753 | class MissingConversionException(ValueError): 754 | pass 755 | 756 | 757 | mp_to_lv = { 758 | "mp_obj_t": "(mp_obj_t)", 759 | "va_list": None, 760 | "void *": "mp_to_ptr", 761 | "const uint8_t *": "mp_to_ptr", 762 | "const void *": "mp_to_ptr", 763 | "bool": "mp_obj_is_true", 764 | "char *": "(char*)convert_from_str", 765 | "char **": "mp_write_ptr_C_Pointer", 766 | "const char *": "convert_from_str", 767 | "const char **": "mp_write_ptr_C_Pointer", 768 | "%s_obj_t *" % module_prefix: "mp_to_lv", 769 | "uint8_t": "(uint8_t)mp_obj_get_int", 770 | "uint16_t": "(uint16_t)mp_obj_get_int", 771 | "uint32_t": "(uint32_t)mp_obj_get_int", 772 | "uint64_t": "(uint64_t)mp_obj_get_ull", 773 | "unsigned": "(unsigned)mp_obj_get_int", 774 | "unsigned int": "(unsigned int)mp_obj_get_int", 775 | "unsigned char": "(unsigned char)mp_obj_get_int", 776 | "unsigned short": "(unsigned short)mp_obj_get_int", 777 | "unsigned long": "(unsigned long)mp_obj_get_int", 778 | "unsigned long int": "(unsigned long int)mp_obj_get_int", 779 | "unsigned long long": "(unsigned long long)mp_obj_get_ull", 780 | "unsigned long long int": "(unsigned long long int)mp_obj_get_ull", 781 | "int8_t": "(int8_t)mp_obj_get_int", 782 | "int16_t": "(int16_t)mp_obj_get_int", 783 | "int32_t": "(int32_t)mp_obj_get_int", 784 | "int64_t": "(int64_t)mp_obj_get_ull", 785 | "size_t": "(size_t)mp_obj_get_int", 786 | "int": "(int)mp_obj_get_int", 787 | "char": "(char)mp_obj_get_int", 788 | "short": "(short)mp_obj_get_int", 789 | "long": "(long)mp_obj_get_int", 790 | "long int": "(long int)mp_obj_get_int", 791 | "long long": "(long long)mp_obj_get_ull", 792 | "long long int": "(long long int)mp_obj_get_ull", 793 | "float": "(float)mp_obj_get_float", 794 | } 795 | 796 | lv_to_mp = { 797 | "mp_obj_t": "(mp_obj_t)", 798 | "va_list": None, 799 | "void *": "ptr_to_mp", 800 | "const uint8_t *": "ptr_to_mp", 801 | "const void *": "ptr_to_mp", 802 | "bool": "convert_to_bool", 803 | "char *": "convert_to_str", 804 | "char **": "mp_read_ptr_C_Pointer", 805 | "const char *": "convert_to_str", 806 | "const char **": "mp_read_ptr_C_Pointer", 807 | "%s_obj_t *" % module_prefix: "lv_to_mp", 808 | "uint8_t": "mp_obj_new_int_from_uint", 809 | "uint16_t": "mp_obj_new_int_from_uint", 810 | "uint32_t": "mp_obj_new_int_from_uint", 811 | "uint64_t": "mp_obj_new_int_from_ull", 812 | "unsigned": "mp_obj_new_int_from_uint", 813 | "unsigned int": "mp_obj_new_int_from_uint", 814 | "unsigned char": "mp_obj_new_int_from_uint", 815 | "unsigned short": "mp_obj_new_int_from_uint", 816 | "unsigned long": "mp_obj_new_int_from_uint", 817 | "unsigned long int": "mp_obj_new_int_from_uint", 818 | "unsigned long long": "mp_obj_new_int_from_ull", 819 | "unsigned long long int": "mp_obj_new_int_from_ull", 820 | "int8_t": "mp_obj_new_int", 821 | "int16_t": "mp_obj_new_int", 822 | "int32_t": "mp_obj_new_int", 823 | "int64_t": "mp_obj_new_int_from_ll", 824 | "size_t": "mp_obj_new_int_from_uint", 825 | "int": "mp_obj_new_int", 826 | "char": "mp_obj_new_int", 827 | "short": "mp_obj_new_int", 828 | "long": "mp_obj_new_int", 829 | "long int": "mp_obj_new_int", 830 | "long long": "mp_obj_new_int_from_ll", 831 | "long long int": "mp_obj_new_int_from_ll", 832 | "float": "mp_obj_new_float", 833 | } 834 | 835 | lv_mp_type = { 836 | "mp_obj_t": "%s*" % base_obj_type, 837 | "va_list": None, 838 | "void *": "void*", 839 | "const uint8_t *": "void*", 840 | "const void *": "void*", 841 | "bool": "bool", 842 | "char *": "char*", 843 | "char **": "char**", 844 | "const char *": "char*", 845 | "const char **": "char**", 846 | "%s_obj_t *" % module_prefix: "%s*" % base_obj_type, 847 | "uint8_t": "int", 848 | "uint16_t": "int", 849 | "uint32_t": "int", 850 | "uint64_t": "int", 851 | "unsigned": "int", 852 | "unsigned int": "int", 853 | "unsigned char": "int", 854 | "unsigned short": "int", 855 | "unsigned long": "int", 856 | "unsigned long int": "int", 857 | "unsigned long long": "int", 858 | "unsigned long long int": "int", 859 | "int8_t": "int", 860 | "int16_t": "int", 861 | "int32_t": "int", 862 | "int64_t": "int", 863 | "size_t": "int", 864 | "int": "int", 865 | "char": "int", 866 | "short": "int", 867 | "long": "int", 868 | "long int": "int", 869 | "long long": "int", 870 | "long long int": "int", 871 | "void": None, 872 | "float": "float", 873 | } 874 | 875 | lv_to_mp_byref = {} 876 | lv_to_mp_funcptr = {} 877 | 878 | # Add native array supported types 879 | # These types would be converted automatically to/from array type. 880 | # Supported array (pointer) types are signed/unsigned int: 8bit, 16bit, 32bit and 64bit. 881 | 882 | 883 | def register_int_ptr_type(convertor, *types): 884 | for ptr_type in types: 885 | for qualified_ptr_type in [ptr_type, "const " + ptr_type]: 886 | mp_to_lv[qualified_ptr_type] = "mp_array_to_%s" % (convertor) 887 | lv_to_mp[qualified_ptr_type] = "mp_array_from_%s" % (convertor) 888 | lv_mp_type[qualified_ptr_type] = "void*" 889 | 890 | 891 | register_int_ptr_type("u8ptr", "unsigned char *", "uint8_t *") 892 | 893 | # char* is considered as string, not int ptr! 894 | """ 895 | register_int_ptr_type('i8ptr', 896 | 'char *', 897 | 'int8_t *') 898 | """ 899 | 900 | register_int_ptr_type("u16ptr", "unsigned short *", "uint16_t *") 901 | 902 | register_int_ptr_type("i16ptr", "short *", "int16_t *") 903 | 904 | register_int_ptr_type( 905 | "u32ptr", 906 | "uint32_t *", 907 | "unsigned *", 908 | "unsigned int *", 909 | "unsigned long *", 910 | "unsigned long int *", 911 | "size_t *", 912 | ) 913 | 914 | register_int_ptr_type( 915 | "i32ptr", 916 | "int32_t *", 917 | "signed *", 918 | "signed int *", 919 | "signed long *", 920 | "signed long int *", 921 | "long *", 922 | "long int *", 923 | "int *", 924 | ) 925 | 926 | register_int_ptr_type( 927 | "u64ptr", "int64_t *", "signed long long *", "long long *", "long long int *" 928 | ) 929 | 930 | register_int_ptr_type( 931 | "i64ptr", "uint64_t *", "unsigned long long *", "unsigned long long int *" 932 | ) 933 | 934 | 935 | # 936 | # Emit Header 937 | # 938 | 939 | headers = args.input 940 | for header in headers: 941 | if "lvgl.h" in header: 942 | path, _ = os.path.split(header) 943 | if path and path != "lvgl.h": 944 | path = os.path.join(path, "src", "lvgl_private.h") 945 | else: 946 | path = "src/lvgl_private.h" 947 | 948 | headers.append(path) 949 | break 950 | 951 | print( 952 | """ 953 | /* 954 | * Auto-Generated file, DO NOT EDIT! 955 | * 956 | * Command line: 957 | * {cmd_line} 958 | * 959 | * Preprocessing command: 960 | * {pp_cmd} 961 | * 962 | * Generating Objects: {objs} 963 | */ 964 | 965 | /* 966 | * Mpy includes 967 | */ 968 | 969 | #include 970 | #include 971 | #include "py/obj.h" 972 | #include "py/objint.h" 973 | #include "py/objstr.h" 974 | #include "py/runtime.h" 975 | #include "py/binary.h" 976 | #include "py/objarray.h" 977 | #include "py/objtype.h" 978 | #include "py/objexcept.h" 979 | 980 | /* 981 | * {module_name} includes 982 | */ 983 | 984 | {lv_headers} 985 | """.format( 986 | module_name=module_name, 987 | cmd_line=" ".join(argv), 988 | pp_cmd=pp_cmd, 989 | objs=", ".join( 990 | ["%s(%s)" % (objname, parent_obj_names[objname]) for objname in obj_names] 991 | ), 992 | lv_headers="\n".join('#include "%s"' % header for header in headers), 993 | ) 994 | ) 995 | 996 | # 997 | # Enable objects, if supported 998 | # 999 | 1000 | if len(obj_names) > 0: 1001 | print( 1002 | """ 1003 | #define LV_OBJ_T {obj_type} 1004 | 1005 | typedef struct mp_lv_obj_type_t {{ 1006 | const lv_obj_class_t *lv_obj_class; 1007 | const mp_obj_type_t *mp_obj_type; 1008 | }} mp_lv_obj_type_t; 1009 | 1010 | static const mp_lv_obj_type_t mp_lv_{base_obj}_type; 1011 | static const mp_lv_obj_type_t *mp_lv_obj_types[]; 1012 | 1013 | static inline const mp_obj_type_t *get_BaseObj_type() 1014 | {{ 1015 | return mp_lv_{base_obj}_type.mp_obj_type; 1016 | }} 1017 | 1018 | MP_DEFINE_EXCEPTION(LvReferenceError, Exception) 1019 | """.format(obj_type=base_obj_type, base_obj=base_obj_name) 1020 | ) 1021 | 1022 | # 1023 | # Emit Mpy helper functions 1024 | # 1025 | 1026 | print( 1027 | """ 1028 | /* 1029 | * Helper functions 1030 | */ 1031 | 1032 | #ifndef GENMPY_UNUSED 1033 | #ifdef __GNUC__ 1034 | #define GENMPY_UNUSED __attribute__ ((unused)) 1035 | #else 1036 | #define GENMPY_UNUSED 1037 | #endif // __GNUC__ 1038 | #endif // GENMPY_UNUSED 1039 | 1040 | // Custom function mp object 1041 | 1042 | typedef mp_obj_t (*mp_fun_ptr_var_t)(size_t n, const mp_obj_t *, void *ptr); 1043 | 1044 | typedef struct mp_lv_obj_fun_builtin_var_t { 1045 | mp_obj_base_t base; 1046 | mp_uint_t n_args; 1047 | mp_fun_ptr_var_t mp_fun; 1048 | void *lv_fun; 1049 | } mp_lv_obj_fun_builtin_var_t; 1050 | 1051 | static mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); 1052 | static mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); 1053 | 1054 | GENMPY_UNUSED static MP_DEFINE_CONST_OBJ_TYPE( 1055 | mp_lv_type_fun_builtin_var, 1056 | MP_QSTR_function, 1057 | MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, 1058 | call, lv_fun_builtin_var_call, 1059 | unary_op, mp_obj_int_unary_op, 1060 | buffer, mp_func_get_buffer 1061 | ); 1062 | 1063 | GENMPY_UNUSED static MP_DEFINE_CONST_OBJ_TYPE( 1064 | mp_lv_type_fun_builtin_static_var, 1065 | MP_QSTR_function, 1066 | MP_TYPE_FLAG_BUILTIN_FUN, 1067 | call, lv_fun_builtin_var_call, 1068 | unary_op, mp_obj_int_unary_op, 1069 | buffer, mp_func_get_buffer 1070 | ); 1071 | 1072 | static mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { 1073 | assert(MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_var) || 1074 | MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_static_var)); 1075 | mp_lv_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); 1076 | mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); 1077 | return self->mp_fun(n_args, args, self->lv_fun); 1078 | } 1079 | 1080 | static mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { 1081 | (void)flags; 1082 | assert(MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_var) || 1083 | MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_static_var)); 1084 | mp_lv_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); 1085 | 1086 | bufinfo->buf = &self->lv_fun; 1087 | bufinfo->len = sizeof(self->lv_fun); 1088 | bufinfo->typecode = BYTEARRAY_TYPECODE; 1089 | return 0; 1090 | } 1091 | 1092 | #define MP_DEFINE_CONST_LV_FUN_OBJ_VAR(obj_name, n_args, mp_fun, lv_fun) \\ 1093 | const mp_lv_obj_fun_builtin_var_t obj_name = \\ 1094 | {{&mp_lv_type_fun_builtin_var}, n_args, mp_fun, lv_fun} 1095 | 1096 | #define MP_DEFINE_CONST_LV_FUN_OBJ_STATIC_VAR(obj_name, n_args, mp_fun, lv_fun) \\ 1097 | const mp_lv_obj_fun_builtin_var_t obj_name = \\ 1098 | {{&mp_lv_type_fun_builtin_static_var}, n_args, mp_fun, lv_fun} 1099 | 1100 | // Casting 1101 | 1102 | typedef struct mp_lv_struct_t 1103 | { 1104 | mp_obj_base_t base; 1105 | void *data; 1106 | } mp_lv_struct_t; 1107 | 1108 | static const mp_lv_struct_t mp_lv_null_obj; 1109 | 1110 | #ifdef LV_OBJ_T 1111 | static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); 1112 | #else 1113 | static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags){ return 0; } 1114 | #endif 1115 | 1116 | static mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); 1117 | 1118 | static mp_obj_t get_native_obj(mp_obj_t mp_obj) 1119 | { 1120 | if (!MP_OBJ_IS_OBJ(mp_obj)) return mp_obj; 1121 | const mp_obj_type_t *native_type = ((mp_obj_base_t*)mp_obj)->type; 1122 | if (native_type == NULL) 1123 | return NULL; 1124 | if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(native_type, parent) == NULL || 1125 | (MP_OBJ_TYPE_GET_SLOT_OR_NULL(native_type, buffer) == mp_blob_get_buffer) || 1126 | (MP_OBJ_TYPE_GET_SLOT_OR_NULL(native_type, buffer) == mp_lv_obj_get_buffer)) 1127 | return mp_obj; 1128 | while (MP_OBJ_TYPE_GET_SLOT_OR_NULL(native_type, parent)) native_type = MP_OBJ_TYPE_GET_SLOT(native_type, parent); 1129 | return mp_obj_cast_to_native_base(mp_obj, MP_OBJ_FROM_PTR(native_type)); 1130 | } 1131 | 1132 | static mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type); 1133 | 1134 | static mp_obj_t make_new_lv_struct( 1135 | const mp_obj_type_t *type, 1136 | size_t n_args, 1137 | size_t n_kw, 1138 | const mp_obj_t *args); 1139 | 1140 | static mp_obj_t cast(mp_obj_t mp_obj, const mp_obj_type_t *mp_type) 1141 | { 1142 | mp_obj_t res = NULL; 1143 | if (mp_obj == mp_const_none && MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_type, make_new) == &make_new_lv_struct) { 1144 | res = MP_OBJ_FROM_PTR(&mp_lv_null_obj); 1145 | } else if (MP_OBJ_IS_OBJ(mp_obj)) { 1146 | res = get_native_obj(mp_obj); 1147 | if (res){ 1148 | const mp_obj_type_t *res_type = ((mp_obj_base_t*)res)->type; 1149 | if (res_type != mp_type){ 1150 | if (res_type == &mp_type_dict && 1151 | MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_type, make_new) == &make_new_lv_struct) 1152 | res = dict_to_struct(res, mp_type); 1153 | else res = NULL; 1154 | } 1155 | } 1156 | } 1157 | if (res == NULL) nlr_raise( 1158 | mp_obj_new_exception_msg_varg( 1159 | &mp_type_SyntaxError, MP_ERROR_TEXT("Can't convert %s to %s!"), mp_obj_get_type_str(mp_obj), qstr_str(mp_type->name))); 1160 | return res; 1161 | } 1162 | 1163 | // object handling 1164 | // This section is enabled only when objects are supported 1165 | 1166 | #ifdef LV_OBJ_T 1167 | 1168 | typedef struct mp_lv_obj_t { 1169 | mp_obj_base_t base; 1170 | LV_OBJ_T *lv_obj; 1171 | LV_OBJ_T *callbacks; 1172 | } mp_lv_obj_t; 1173 | 1174 | static inline LV_OBJ_T *mp_to_lv(mp_obj_t mp_obj) 1175 | { 1176 | if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; 1177 | mp_obj_t native_obj = get_native_obj(mp_obj); 1178 | if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(native_obj), buffer) != mp_lv_obj_get_buffer) 1179 | return NULL; 1180 | mp_lv_obj_t *mp_lv_obj = MP_OBJ_TO_PTR(native_obj); 1181 | if (mp_lv_obj->lv_obj == NULL) { 1182 | nlr_raise( 1183 | mp_obj_new_exception_msg( 1184 | &mp_type_LvReferenceError, MP_ERROR_TEXT("Referenced object was deleted!"))); 1185 | } 1186 | return mp_lv_obj->lv_obj; 1187 | } 1188 | 1189 | static inline LV_OBJ_T *mp_get_callbacks(mp_obj_t mp_obj) 1190 | { 1191 | if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; 1192 | mp_lv_obj_t *mp_lv_obj = MP_OBJ_TO_PTR(get_native_obj(mp_obj)); 1193 | if (mp_lv_obj == NULL) 1194 | nlr_raise( 1195 | mp_obj_new_exception_msg( 1196 | &mp_type_SyntaxError, MP_ERROR_TEXT("'user_data' argument must be either a dict or None!"))); 1197 | if (!mp_lv_obj->callbacks) mp_lv_obj->callbacks = mp_obj_new_dict(0); 1198 | return mp_lv_obj->callbacks; 1199 | } 1200 | 1201 | static inline const mp_obj_type_t *get_BaseObj_type(); 1202 | 1203 | static void mp_lv_delete_cb(lv_event_t * e) 1204 | { 1205 | LV_OBJ_T *lv_obj = e->current_target; 1206 | if (lv_obj){ 1207 | mp_lv_obj_t *self = lv_obj->user_data; 1208 | if (self) { 1209 | self->lv_obj = NULL; 1210 | } 1211 | } 1212 | } 1213 | 1214 | static inline mp_obj_t lv_to_mp(LV_OBJ_T *lv_obj) 1215 | { 1216 | if (lv_obj == NULL) return mp_const_none; 1217 | mp_lv_obj_t *self = (mp_lv_obj_t*)lv_obj->user_data; 1218 | if (!self) 1219 | { 1220 | // Find the object type 1221 | const mp_obj_type_t *mp_obj_type = get_BaseObj_type(); 1222 | const lv_obj_class_t *lv_obj_class = lv_obj_get_class(lv_obj); 1223 | const mp_lv_obj_type_t **iter = &mp_lv_obj_types[0]; 1224 | for (; *iter; iter++) { 1225 | if ((*iter)->lv_obj_class == lv_obj_class) { 1226 | mp_obj_type = (*iter)->mp_obj_type; 1227 | break; 1228 | } 1229 | } 1230 | 1231 | // Create the MP object 1232 | self = m_new_obj(mp_lv_obj_t); 1233 | *self = (mp_lv_obj_t){ 1234 | .base = {(const mp_obj_type_t *)mp_obj_type}, 1235 | .lv_obj = lv_obj, 1236 | .callbacks = NULL, 1237 | }; 1238 | 1239 | // Register the Python object in user_data 1240 | lv_obj->user_data = self; 1241 | 1242 | // Register a "Delete" event callback 1243 | lv_obj_add_event_cb(lv_obj, mp_lv_delete_cb, LV_EVENT_DELETE, NULL); 1244 | } 1245 | return MP_OBJ_FROM_PTR(self); 1246 | } 1247 | 1248 | static void* mp_to_ptr(mp_obj_t self_in); 1249 | 1250 | static mp_obj_t cast_obj_type(const mp_obj_type_t* type, mp_obj_t obj) 1251 | { 1252 | mp_lv_obj_t *self = m_new_obj(mp_lv_obj_t); 1253 | *self = (mp_lv_obj_t){ 1254 | .base = {type}, 1255 | .lv_obj = mp_to_ptr(obj), 1256 | .callbacks = NULL, 1257 | }; 1258 | if (!self->lv_obj) return mp_const_none; 1259 | return MP_OBJ_FROM_PTR(self); 1260 | } 1261 | 1262 | static mp_obj_t cast_obj(mp_obj_t type_obj, mp_obj_t obj) 1263 | { 1264 | return cast_obj_type((const mp_obj_type_t *)type_obj, obj); 1265 | } 1266 | 1267 | static mp_obj_t make_new( 1268 | const mp_lv_obj_fun_builtin_var_t *lv_obj_var, 1269 | const mp_obj_type_t *type, 1270 | size_t n_args, 1271 | size_t n_kw, 1272 | const mp_obj_t *args) 1273 | { 1274 | mp_obj_t lv_obj; 1275 | if (n_args == 0 && n_kw == 0) // allow no args, and pass NULL as parent in such case 1276 | { 1277 | const mp_obj_t no_args[] = {mp_const_none}; 1278 | lv_obj = mp_call_function_n_kw(MP_OBJ_FROM_PTR(lv_obj_var), 1, 0, no_args); 1279 | } 1280 | else 1281 | { 1282 | lv_obj = mp_call_function_n_kw(MP_OBJ_FROM_PTR(lv_obj_var), n_args, n_kw, args); 1283 | } 1284 | 1285 | if (!lv_obj) return mp_const_none; 1286 | 1287 | mp_lv_obj_t *self = MP_OBJ_TO_PTR(lv_obj); 1288 | if (self->base.type != type) 1289 | return cast_obj_type(type, lv_obj); 1290 | return lv_obj; 1291 | } 1292 | 1293 | static MP_DEFINE_CONST_FUN_OBJ_2(cast_obj_obj, cast_obj); 1294 | static MP_DEFINE_CONST_CLASSMETHOD_OBJ(cast_obj_class_method, MP_ROM_PTR(&cast_obj_obj)); 1295 | 1296 | static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { 1297 | (void)flags; 1298 | mp_lv_obj_t *self = MP_OBJ_TO_PTR(self_in); 1299 | 1300 | bufinfo->buf = &self->lv_obj; 1301 | bufinfo->len = sizeof(self->lv_obj); 1302 | bufinfo->typecode = BYTEARRAY_TYPECODE; 1303 | return 0; 1304 | } 1305 | 1306 | static mp_obj_t mp_lv_obj_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) 1307 | { 1308 | mp_lv_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); 1309 | mp_lv_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); 1310 | switch (op) 1311 | { 1312 | case MP_BINARY_OP_EQUAL: 1313 | return mp_obj_new_bool(lhs->lv_obj == rhs->lv_obj); 1314 | case MP_BINARY_OP_NOT_EQUAL: 1315 | return mp_obj_new_bool(lhs->lv_obj != rhs->lv_obj); 1316 | default: 1317 | return MP_OBJ_NULL; 1318 | } 1319 | } 1320 | 1321 | // Register LVGL root pointers 1322 | MP_REGISTER_ROOT_POINTER(void *mp_lv_roots); 1323 | MP_REGISTER_ROOT_POINTER(void *mp_lv_user_data); 1324 | 1325 | void *mp_lv_roots; 1326 | 1327 | void mp_lv_init_gc() 1328 | { 1329 | static bool mp_lv_roots_initialized = false; 1330 | if (!mp_lv_roots_initialized) { 1331 | mp_lv_roots = MP_STATE_VM(mp_lv_roots) = m_new0(lv_global_t, 1); 1332 | mp_lv_roots_initialized = true; 1333 | } 1334 | } 1335 | 1336 | #else // LV_OBJ_T 1337 | 1338 | typedef struct mp_lv_obj_type_t { 1339 | mp_obj_type_t *mp_obj_type; 1340 | } mp_lv_obj_type_t; 1341 | 1342 | #endif 1343 | 1344 | static inline mp_obj_t convert_to_bool(bool b) 1345 | { 1346 | return b? mp_const_true: mp_const_false; 1347 | } 1348 | 1349 | static inline mp_obj_t convert_to_str(const char *str) 1350 | { 1351 | return str? mp_obj_new_str(str, strlen(str)): mp_const_none; 1352 | } 1353 | 1354 | static inline const char *convert_from_str(mp_obj_t str) 1355 | { 1356 | if (str == NULL || str == mp_const_none) 1357 | return NULL; 1358 | 1359 | if (MP_OBJ_IS_TYPE(str, &mp_type_bytearray) || 1360 | MP_OBJ_IS_TYPE(str, &mp_type_memoryview)) { 1361 | mp_buffer_info_t buffer_info; 1362 | if (mp_get_buffer(str, &buffer_info, MP_BUFFER_READ)) { 1363 | return buffer_info.buf; 1364 | } 1365 | } 1366 | 1367 | return mp_obj_str_get_str(str); 1368 | } 1369 | 1370 | // struct handling 1371 | 1372 | static mp_lv_struct_t *mp_to_lv_struct(mp_obj_t mp_obj) 1373 | { 1374 | if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; 1375 | mp_obj_t native_obj = get_native_obj(mp_obj); 1376 | if ( (!MP_OBJ_IS_OBJ(native_obj)) || (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(native_obj), make_new) != &make_new_lv_struct) ) nlr_raise( 1377 | mp_obj_new_exception_msg( 1378 | &mp_type_SyntaxError, MP_ERROR_TEXT("Expected Struct object!"))); 1379 | mp_lv_struct_t *mp_lv_struct = MP_OBJ_TO_PTR(native_obj); 1380 | return mp_lv_struct; 1381 | } 1382 | 1383 | static inline size_t get_lv_struct_size(const mp_obj_type_t *type) 1384 | { 1385 | mp_obj_dict_t *self = MP_OBJ_TO_PTR(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)); 1386 | mp_map_elem_t *elem = mp_map_lookup(&self->map, MP_OBJ_NEW_QSTR(MP_QSTR___SIZE__), MP_MAP_LOOKUP); 1387 | if (elem == NULL) { 1388 | return 0; 1389 | } else { 1390 | return (size_t)mp_obj_get_int(elem->value); 1391 | } 1392 | } 1393 | 1394 | static mp_obj_t make_new_lv_struct( 1395 | const mp_obj_type_t *type, 1396 | size_t n_args, 1397 | size_t n_kw, 1398 | const mp_obj_t *args) 1399 | { 1400 | if ((!MP_OBJ_IS_TYPE(type, &mp_type_type)) || MP_OBJ_TYPE_GET_SLOT_OR_NULL(type, make_new) != &make_new_lv_struct) 1401 | nlr_raise( 1402 | mp_obj_new_exception_msg( 1403 | &mp_type_SyntaxError, MP_ERROR_TEXT("Argument is not a struct type!"))); 1404 | size_t size = get_lv_struct_size(type); 1405 | mp_arg_check_num(n_args, n_kw, 0, 1, false); 1406 | mp_lv_struct_t *self = m_new_obj(mp_lv_struct_t); 1407 | mp_lv_struct_t *other = (n_args > 0) && (!mp_obj_is_int(args[0])) ? mp_to_lv_struct(cast(args[0], type)): NULL; 1408 | size_t count = (n_args > 0) && (mp_obj_is_int(args[0]))? mp_obj_get_int(args[0]): 1; 1409 | *self = (mp_lv_struct_t){ 1410 | .base = {type}, 1411 | .data = (size == 0 || (other && other->data == NULL))? NULL: m_malloc(size * count) 1412 | }; 1413 | if (self->data) { 1414 | if (other) { 1415 | memcpy(self->data, other->data, size * count); 1416 | } else { 1417 | memset(self->data, 0, size * count); 1418 | } 1419 | } 1420 | return MP_OBJ_FROM_PTR(self); 1421 | } 1422 | 1423 | static mp_obj_t lv_struct_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) 1424 | { 1425 | mp_lv_struct_t *lhs = MP_OBJ_TO_PTR(lhs_in); 1426 | mp_lv_struct_t *rhs = MP_OBJ_TO_PTR(rhs_in); 1427 | switch (op) 1428 | { 1429 | case MP_BINARY_OP_EQUAL: 1430 | return mp_obj_new_bool(lhs->data == rhs->data); 1431 | case MP_BINARY_OP_NOT_EQUAL: 1432 | return mp_obj_new_bool(lhs->data != rhs->data); 1433 | default: 1434 | return MP_OBJ_NULL; 1435 | } 1436 | } 1437 | 1438 | static mp_obj_t lv_struct_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) 1439 | { 1440 | mp_lv_struct_t *self = mp_to_lv_struct(self_in); 1441 | 1442 | if ((!self) || (!self->data)) 1443 | return NULL; 1444 | if (!mp_obj_is_int(index)) { 1445 | nlr_raise( 1446 | mp_obj_new_exception_msg( 1447 | &mp_type_SyntaxError, MP_ERROR_TEXT("Subscript index must be an integer!"))); 1448 | } 1449 | 1450 | const mp_obj_type_t *type = mp_obj_get_type(self_in); 1451 | size_t element_size = get_lv_struct_size(type); 1452 | if (element_size == 0) return mp_const_none; 1453 | size_t element_index = mp_obj_get_int(index); 1454 | void *element_addr = (byte*)self->data + element_size*element_index; 1455 | 1456 | if (value == MP_OBJ_NULL) { 1457 | memset(element_addr, 0, element_size); 1458 | return self_in; 1459 | } 1460 | 1461 | mp_lv_struct_t *element_at_index = m_new_obj(mp_lv_struct_t); 1462 | *element_at_index = (mp_lv_struct_t){ 1463 | .base = {type}, 1464 | .data = element_addr 1465 | }; 1466 | 1467 | if (value != MP_OBJ_SENTINEL){ 1468 | mp_lv_struct_t *other = mp_to_lv_struct(cast(value, type)); 1469 | if ((!other) || (!other->data)) 1470 | return NULL; 1471 | memcpy(element_at_index->data, other->data, element_size); 1472 | } 1473 | 1474 | return MP_OBJ_FROM_PTR(element_at_index); 1475 | } 1476 | 1477 | GENMPY_UNUSED static void *copy_buffer(const void *buffer, size_t size) 1478 | { 1479 | void *new_buffer = m_malloc(size); 1480 | memcpy(new_buffer, buffer, size); 1481 | return new_buffer; 1482 | } 1483 | 1484 | // Reference an existing lv struct (or part of it) 1485 | 1486 | static mp_obj_t lv_to_mp_struct(const mp_obj_type_t *type, void *lv_struct) 1487 | { 1488 | if (lv_struct == NULL) return mp_const_none; 1489 | mp_lv_struct_t *self = m_new_obj(mp_lv_struct_t); 1490 | *self = (mp_lv_struct_t){ 1491 | .base = {type}, 1492 | .data = lv_struct 1493 | }; 1494 | return MP_OBJ_FROM_PTR(self); 1495 | } 1496 | 1497 | static void call_parent_methods(mp_obj_t obj, qstr attr, mp_obj_t *dest) 1498 | { 1499 | const mp_obj_type_t *type = mp_obj_get_type(obj); 1500 | while (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { 1501 | // generic method lookup 1502 | // this is a lookup in the object (ie not class or type) 1503 | assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now 1504 | mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; 1505 | mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); 1506 | if (elem != NULL) { 1507 | mp_convert_member_lookup(obj, type, elem->value, dest); 1508 | break; 1509 | } 1510 | if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(type, parent) == NULL) { 1511 | break; 1512 | } 1513 | // search parents 1514 | type = MP_OBJ_TYPE_GET_SLOT(type, parent); 1515 | } 1516 | } 1517 | 1518 | // Convert dict to struct 1519 | 1520 | static mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type) 1521 | { 1522 | mp_obj_t mp_struct = make_new_lv_struct(type, 0, 0, NULL); 1523 | mp_obj_t native_dict = cast(dict, &mp_type_dict); 1524 | mp_map_t *map = mp_obj_dict_get_map(native_dict); 1525 | if (map == NULL) return mp_const_none; 1526 | for (uint i = 0; i < map->alloc; i++) { 1527 | mp_obj_t key = map->table[i].key; 1528 | mp_obj_t value = map->table[i].value; 1529 | if (key != MP_OBJ_NULL) { 1530 | mp_obj_t dest[] = {MP_OBJ_SENTINEL, value}; 1531 | MP_OBJ_TYPE_GET_SLOT(type, attr)(mp_struct, mp_obj_str_get_qstr(key), dest); 1532 | if (dest[0]) nlr_raise( 1533 | mp_obj_new_exception_msg_varg( 1534 | &mp_type_SyntaxError, MP_ERROR_TEXT("Cannot set field %s on struct %s!"), qstr_str(mp_obj_str_get_qstr(key)), qstr_str(type->name))); 1535 | } 1536 | } 1537 | return mp_struct; 1538 | } 1539 | 1540 | // Convert mp object to ptr 1541 | 1542 | static void* mp_to_ptr(mp_obj_t self_in) 1543 | { 1544 | mp_buffer_info_t buffer_info; 1545 | if (self_in == NULL || self_in == mp_const_none) 1546 | return NULL; 1547 | 1548 | // if (MP_OBJ_IS_INT(self_in)) 1549 | // return (void*)mp_obj_get_int(self_in); 1550 | 1551 | // If an object is user instance, take it as is so it could be used as user_data 1552 | if (mp_obj_is_instance_type(mp_obj_get_type(self_in))){ 1553 | return MP_OBJ_TO_PTR(self_in); 1554 | } 1555 | 1556 | if (!mp_get_buffer(self_in, &buffer_info, MP_BUFFER_READ)) { 1557 | // No buffer protocol - this is not a Struct or a Blob, it's some other mp object. 1558 | // We only allow setting dict directly, since it's useful to setting user_data for passing data to C. 1559 | // On other cases throw an exception, to avoid a crash later 1560 | if (MP_OBJ_IS_TYPE(self_in, &mp_type_dict)) 1561 | return MP_OBJ_TO_PTR(self_in); 1562 | else nlr_raise( 1563 | mp_obj_new_exception_msg_varg( 1564 | &mp_type_SyntaxError, MP_ERROR_TEXT("Cannot convert '%s' to pointer!"), mp_obj_get_type_str(self_in))); 1565 | } 1566 | 1567 | if (MP_OBJ_IS_STR_OR_BYTES(self_in) || 1568 | MP_OBJ_IS_TYPE(self_in, &mp_type_bytearray) || 1569 | MP_OBJ_IS_TYPE(self_in, &mp_type_memoryview)) 1570 | return buffer_info.buf; 1571 | else 1572 | { 1573 | void *result; 1574 | if (buffer_info.len != sizeof(result) || buffer_info.typecode != BYTEARRAY_TYPECODE){ 1575 | nlr_raise( 1576 | mp_obj_new_exception_msg_varg( 1577 | &mp_type_SyntaxError, MP_ERROR_TEXT("Cannot convert %s to pointer! (buffer does not represent a pointer)"), mp_obj_get_type_str(self_in))); 1578 | } 1579 | memcpy(&result, buffer_info.buf, sizeof(result)); 1580 | return result; 1581 | } 1582 | } 1583 | 1584 | // Blob is a wrapper for void* 1585 | 1586 | static void mp_blob_print(const mp_print_t *print, 1587 | mp_obj_t self_in, 1588 | mp_print_kind_t kind) 1589 | { 1590 | mp_printf(print, "Blob"); 1591 | } 1592 | 1593 | static mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { 1594 | (void)flags; 1595 | mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); 1596 | 1597 | bufinfo->buf = &self->data; 1598 | bufinfo->len = sizeof(self->data); 1599 | bufinfo->typecode = BYTEARRAY_TYPECODE; 1600 | return 0; 1601 | } 1602 | 1603 | static const mp_obj_fun_builtin_var_t mp_lv_dereference_obj; 1604 | 1605 | // Sometimes (but not always!) Blob represents a Micropython object. 1606 | // In such cases it's safe to cast the Blob back to the Micropython object 1607 | // cast argument is the underlying object type, and it's optional. 1608 | 1609 | static mp_obj_t mp_blob_cast(size_t argc, const mp_obj_t *argv) 1610 | { 1611 | mp_obj_t self = argv[0]; 1612 | void *ptr = mp_to_ptr(self); 1613 | if (argc == 1) return MP_OBJ_FROM_PTR(ptr); 1614 | mp_obj_t type = argv[1]; 1615 | if (!MP_OBJ_IS_TYPE(type, &mp_type_type)) 1616 | nlr_raise( 1617 | mp_obj_new_exception_msg( 1618 | &mp_type_SyntaxError, MP_ERROR_TEXT("Cast argument must be a type!"))); 1619 | return cast(MP_OBJ_FROM_PTR(ptr), type); 1620 | } 1621 | 1622 | static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_blob_cast_obj, 1, 2, mp_blob_cast); 1623 | 1624 | static const mp_rom_map_elem_t mp_blob_locals_dict_table[] = { 1625 | { MP_ROM_QSTR(MP_QSTR___dereference__), MP_ROM_PTR(&mp_lv_dereference_obj) }, 1626 | { MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&mp_blob_cast_obj) }, 1627 | }; 1628 | 1629 | static MP_DEFINE_CONST_DICT(mp_blob_locals_dict, mp_blob_locals_dict_table); 1630 | 1631 | static MP_DEFINE_CONST_OBJ_TYPE( 1632 | mp_blob_type, 1633 | MP_QSTR_Blob, 1634 | MP_TYPE_FLAG_NONE, 1635 | binary_op, lv_struct_binary_op, 1636 | print, mp_blob_print, 1637 | locals_dict, &mp_blob_locals_dict, 1638 | buffer, mp_blob_get_buffer 1639 | ); 1640 | 1641 | static const mp_lv_struct_t mp_lv_null_obj = { {&mp_blob_type}, NULL }; 1642 | 1643 | static inline mp_obj_t ptr_to_mp(void *data) 1644 | { 1645 | return lv_to_mp_struct(&mp_blob_type, data); 1646 | } 1647 | 1648 | // Cast pointer to struct 1649 | 1650 | static mp_obj_t mp_lv_cast(mp_obj_t type_obj, mp_obj_t ptr_obj) 1651 | { 1652 | void *ptr = mp_to_ptr(ptr_obj); 1653 | if (!ptr) return mp_const_none; 1654 | mp_lv_struct_t *self = m_new_obj(mp_lv_struct_t); 1655 | *self = (mp_lv_struct_t){ 1656 | .base = {(const mp_obj_type_t*)type_obj}, 1657 | .data = ptr 1658 | }; 1659 | return MP_OBJ_FROM_PTR(self); 1660 | } 1661 | 1662 | // Cast instance. Can be used in ISR when memory allocation is prohibited 1663 | 1664 | static inline mp_obj_t mp_lv_cast_instance(mp_obj_t self_in, mp_obj_t ptr_obj) 1665 | { 1666 | mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); 1667 | self->data = mp_to_ptr(ptr_obj); 1668 | return self_in; 1669 | } 1670 | 1671 | static MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_obj, mp_lv_cast); 1672 | static MP_DEFINE_CONST_CLASSMETHOD_OBJ(mp_lv_cast_class_method, MP_ROM_PTR(&mp_lv_cast_obj)); 1673 | 1674 | static MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_instance_obj, mp_lv_cast_instance); 1675 | 1676 | // Dereference a struct/blob. This allows access to the raw data the struct holds 1677 | 1678 | static mp_obj_t mp_lv_dereference(size_t argc, const mp_obj_t *argv) 1679 | { 1680 | mp_obj_t self_in = argv[0]; 1681 | mp_obj_t size_in = argc > 1? argv[1]: mp_const_none; 1682 | mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); 1683 | size_t size = 0; 1684 | if (size_in == mp_const_none){ 1685 | const mp_obj_type_t *type = self->base.type; 1686 | size = get_lv_struct_size(type); 1687 | } else { 1688 | size = (size_t)mp_obj_get_int(size_in); 1689 | } 1690 | if (size == 0) return mp_const_none; 1691 | mp_obj_array_t *view = MP_OBJ_TO_PTR(mp_obj_new_memoryview(BYTEARRAY_TYPECODE, 1692 | size, self->data)); 1693 | view->typecode |= 0x80; // used to indicate writable buffer 1694 | return MP_OBJ_FROM_PTR(view); 1695 | } 1696 | 1697 | static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_lv_dereference_obj, 1, 2, mp_lv_dereference); 1698 | 1699 | // Callback function handling 1700 | // Callback is either a callable object or a pointer. If it's a callable object, set user_data to the callback. 1701 | // Multiple callbacks are kept per object/struct using a dict that associate callback name with callback object 1702 | // In case of an lv_obj_t, user_data is mp_lv_obj_t which contains a member "callbacks" for that dict. 1703 | // In case of a struct, user_data is a pointer to that dict directly 1704 | 1705 | static mp_obj_t get_callback_dict_from_user_data(void *user_data) 1706 | { 1707 | if (user_data){ 1708 | mp_obj_t obj = MP_OBJ_FROM_PTR(user_data); 1709 | #ifdef LV_OBJ_T 1710 | return 1711 | MP_OBJ_IS_TYPE(obj, &mp_type_dict)? obj: // Handle the case of dict for a struct 1712 | mp_get_callbacks(obj); // Handle the case of mp_lv_obj_t for an lv_obj_t 1713 | #else 1714 | return obj; 1715 | #endif 1716 | } 1717 | return NULL; 1718 | } 1719 | 1720 | typedef void *(*mp_lv_get_user_data)(void *); 1721 | typedef void (*mp_lv_set_user_data)(void *, void *); 1722 | 1723 | static void *mp_lv_callback(mp_obj_t mp_callback, void *lv_callback, qstr callback_name, 1724 | void **user_data_ptr, void *containing_struct, mp_lv_get_user_data get_user_data, mp_lv_set_user_data set_user_data) 1725 | { 1726 | if (lv_callback && mp_obj_is_callable(mp_callback)) { 1727 | void *user_data = NULL; 1728 | if (user_data_ptr) { 1729 | // user_data is either a dict of callbacks in case of struct, or a pointer to mp_lv_obj_t in case of lv_obj_t 1730 | if (! (*user_data_ptr) ) *user_data_ptr = MP_OBJ_TO_PTR(mp_obj_new_dict(0)); // if it's NULL - it's a dict for a struct 1731 | user_data = *user_data_ptr; 1732 | } 1733 | else if (get_user_data && set_user_data) { 1734 | user_data = get_user_data(containing_struct); 1735 | if (!user_data) { 1736 | user_data = MP_OBJ_TO_PTR(mp_obj_new_dict(0)); 1737 | set_user_data(containing_struct, user_data); 1738 | } 1739 | } 1740 | 1741 | if (user_data) { 1742 | mp_obj_t callbacks = get_callback_dict_from_user_data(user_data); 1743 | mp_obj_dict_store(callbacks, MP_OBJ_NEW_QSTR(callback_name), mp_callback); 1744 | } 1745 | return lv_callback; 1746 | } else { 1747 | return mp_to_ptr(mp_callback); 1748 | } 1749 | } 1750 | 1751 | static int _nesting = 0; 1752 | 1753 | // Function pointers wrapper 1754 | 1755 | static mp_obj_t mp_lv_funcptr(const mp_lv_obj_fun_builtin_var_t *mp_fun, void *lv_fun, void *lv_callback, qstr func_name, void *user_data) 1756 | { 1757 | if (lv_fun == NULL) 1758 | return mp_const_none; 1759 | if (lv_fun == lv_callback) { 1760 | mp_obj_t callbacks = get_callback_dict_from_user_data(user_data); 1761 | if (callbacks) 1762 | return mp_obj_dict_get(callbacks, MP_OBJ_NEW_QSTR(func_name)); 1763 | } 1764 | mp_lv_obj_fun_builtin_var_t *funcptr = m_new_obj(mp_lv_obj_fun_builtin_var_t); 1765 | *funcptr = *mp_fun; 1766 | funcptr->lv_fun = lv_fun; 1767 | return MP_OBJ_FROM_PTR(funcptr); 1768 | } 1769 | 1770 | // Missing implementation for 64bit integer conversion 1771 | 1772 | static unsigned long long mp_obj_get_ull(mp_obj_t obj) 1773 | { 1774 | if (mp_obj_is_small_int(obj)) 1775 | return MP_OBJ_SMALL_INT_VALUE(obj); 1776 | 1777 | unsigned long long val = 0; 1778 | bool big_endian = !(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); 1779 | mp_obj_int_to_bytes_impl(obj, big_endian, sizeof(val), (byte*)&val); 1780 | return val; 1781 | } 1782 | 1783 | 1784 | // Array of natives 1785 | 1786 | typedef struct mp_lv_array_t 1787 | { 1788 | mp_lv_struct_t base; 1789 | size_t element_size; 1790 | bool is_signed; 1791 | } mp_lv_array_t; 1792 | 1793 | static void mp_lv_array_print(const mp_print_t *print, 1794 | mp_obj_t self_in, 1795 | mp_print_kind_t kind) 1796 | { 1797 | mp_lv_array_t *self = MP_OBJ_TO_PTR(self_in); 1798 | size_t element_size = self->element_size; 1799 | bool is_signed = self->is_signed; 1800 | mp_printf(print, "C Array (%sint%d[])", is_signed? "": "u", element_size*8); 1801 | } 1802 | 1803 | static mp_obj_t lv_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) 1804 | { 1805 | mp_lv_array_t *self = MP_OBJ_TO_PTR(self_in); 1806 | 1807 | if ((!self) || (!self->base.data)) 1808 | return NULL; 1809 | if (!mp_obj_is_int(index)) { 1810 | nlr_raise( 1811 | mp_obj_new_exception_msg( 1812 | &mp_type_SyntaxError, MP_ERROR_TEXT("Subscript index must be an integer!"))); 1813 | } 1814 | 1815 | size_t element_size = self->element_size; 1816 | size_t element_index = mp_obj_get_int(index); 1817 | void *element_addr = (byte*)self->base.data + element_size*element_index; 1818 | bool is_signed = self->is_signed; 1819 | union { 1820 | long long val; 1821 | unsigned long long uval; 1822 | } element; 1823 | memset(&element, 0, sizeof(element)); 1824 | 1825 | if (value == MP_OBJ_NULL){ 1826 | memset(element_addr, 0, element_size); 1827 | } 1828 | else if (value == MP_OBJ_SENTINEL){ 1829 | memcpy(&element, element_addr, element_size); 1830 | return is_signed? mp_obj_new_int_from_ll(element.val): mp_obj_new_int_from_ull(element.uval); 1831 | } else { 1832 | if (!mp_obj_is_int(value)) { 1833 | nlr_raise( 1834 | mp_obj_new_exception_msg_varg( 1835 | &mp_type_SyntaxError, MP_ERROR_TEXT("Value '%s' must be an integer!"), mp_obj_get_type_str(value))); 1836 | } 1837 | element.uval = mp_obj_get_ull(value); 1838 | memcpy(element_addr, &element, element_size); 1839 | } 1840 | 1841 | return self_in; 1842 | } 1843 | 1844 | static const mp_rom_map_elem_t mp_base_struct_locals_dict_table[] = { 1845 | { MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&mp_lv_cast_class_method) }, 1846 | { MP_ROM_QSTR(MP_QSTR___cast_instance__), MP_ROM_PTR(&mp_lv_cast_instance_obj) }, 1847 | { MP_ROM_QSTR(MP_QSTR___dereference__), MP_ROM_PTR(&mp_lv_dereference_obj) }, 1848 | }; 1849 | 1850 | static MP_DEFINE_CONST_DICT(mp_base_struct_locals_dict, mp_base_struct_locals_dict_table); 1851 | 1852 | static MP_DEFINE_CONST_OBJ_TYPE( 1853 | mp_lv_base_struct_type, 1854 | MP_QSTR_Struct, 1855 | MP_TYPE_FLAG_NONE, 1856 | binary_op, lv_struct_binary_op, 1857 | subscr, lv_struct_subscr, 1858 | buffer, mp_blob_get_buffer, 1859 | locals_dict, &mp_base_struct_locals_dict 1860 | ); 1861 | 1862 | // TODO: provide constructor 1863 | static MP_DEFINE_CONST_OBJ_TYPE( 1864 | mp_lv_array_type, 1865 | MP_QSTR_C_Array, 1866 | MP_TYPE_FLAG_NONE, 1867 | print, mp_lv_array_print, 1868 | binary_op, lv_struct_binary_op, 1869 | subscr, lv_array_subscr, 1870 | buffer, mp_blob_get_buffer, 1871 | locals_dict, &mp_base_struct_locals_dict 1872 | ); 1873 | 1874 | GENMPY_UNUSED static mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) 1875 | { 1876 | mp_lv_array_t *self = m_new_obj(mp_lv_array_t); 1877 | *self = (mp_lv_array_t){ 1878 | { {&mp_lv_array_type}, lv_arr }, 1879 | element_size, 1880 | is_signed 1881 | }; 1882 | return MP_OBJ_FROM_PTR(self); 1883 | } 1884 | 1885 | GENMPY_UNUSED static void *mp_array_to_ptr(mp_obj_t *mp_arr, size_t element_size, GENMPY_UNUSED bool is_signed) 1886 | { 1887 | if (MP_OBJ_IS_STR_OR_BYTES(mp_arr) || 1888 | MP_OBJ_IS_TYPE(mp_arr, &mp_type_bytearray) || 1889 | MP_OBJ_IS_TYPE(mp_arr, &mp_type_memoryview)){ 1890 | return mp_to_ptr(mp_arr); 1891 | } 1892 | 1893 | mp_obj_t mp_len = mp_obj_len_maybe(mp_arr); 1894 | if (mp_len == MP_OBJ_NULL) return mp_to_ptr(mp_arr); 1895 | mp_int_t len = mp_obj_get_int(mp_len); 1896 | void *lv_arr = m_malloc(len * element_size); 1897 | byte *element_addr = (byte*)lv_arr; 1898 | mp_obj_t iter = mp_getiter(mp_arr, NULL); 1899 | mp_obj_t item; 1900 | while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { 1901 | union { 1902 | long long val; 1903 | unsigned long long uval; 1904 | } element; 1905 | if (!mp_obj_is_int(item)) { 1906 | nlr_raise( 1907 | mp_obj_new_exception_msg_varg( 1908 | &mp_type_SyntaxError, MP_ERROR_TEXT("Value '%s' must be an integer!"), mp_obj_get_type_str(item))); 1909 | } 1910 | element.uval = mp_obj_get_ull(item); 1911 | memcpy(element_addr, &element, element_size); 1912 | element_addr += element_size; 1913 | } 1914 | return lv_arr; 1915 | } 1916 | 1917 | #define MP_ARRAY_CONVERTOR(name, size, is_signed) \ 1918 | GENMPY_UNUSED static mp_obj_t mp_array_from_ ## name(void *lv_arr)\ 1919 | {\ 1920 | return mp_array_from_ptr(lv_arr, size, is_signed);\ 1921 | }\ 1922 | GENMPY_UNUSED static void *mp_array_to_ ## name(mp_obj_t mp_arr)\ 1923 | {\ 1924 | return mp_array_to_ptr(mp_arr, size, is_signed);\ 1925 | } 1926 | 1927 | MP_ARRAY_CONVERTOR(u8ptr, 1, false) 1928 | MP_ARRAY_CONVERTOR(i8ptr, 1, true) 1929 | MP_ARRAY_CONVERTOR(u16ptr, 2, false) 1930 | MP_ARRAY_CONVERTOR(i16ptr, 2, true) 1931 | MP_ARRAY_CONVERTOR(u32ptr, 4, false) 1932 | MP_ARRAY_CONVERTOR(i32ptr, 4, true) 1933 | MP_ARRAY_CONVERTOR(u64ptr, 8, false) 1934 | MP_ARRAY_CONVERTOR(i64ptr, 8, true) 1935 | 1936 | """ 1937 | ) 1938 | 1939 | # 1940 | # Add regular enums with integer values 1941 | # 1942 | 1943 | enums = collections.OrderedDict() 1944 | for enum_def in enum_defs: 1945 | # Skip stdatomic.h memory_order, no bindings needed. 1946 | if isinstance(enum_def, c_ast.TypeDecl) and enum_def.declname == "memory_order": 1947 | continue 1948 | 1949 | # eprint("--> %s" % enum_def) 1950 | while hasattr(enum_def.type, "name") and not enum_def.type.values: 1951 | enum_def = next( 1952 | e 1953 | for e in enum_defs 1954 | if hasattr(e.type, "name") 1955 | and e.type.name == enum_def.type.name 1956 | and e.type.values 1957 | ) 1958 | member_names = [ 1959 | member.name 1960 | for member in enum_def.type.values.enumerators 1961 | if not member.name.startswith("_") 1962 | ] 1963 | enum_name = commonprefix(member_names) 1964 | enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix 1965 | enum = collections.OrderedDict() 1966 | for member in enum_def.type.values.enumerators: 1967 | if member.name.startswith("_"): 1968 | continue 1969 | member_name = ( 1970 | member.name[len(enum_name) + 1 :] if len(enum_name) > 0 else member.name 1971 | ) 1972 | if member_name[0].isdigit(): 1973 | member_name = "_" + member_name 1974 | if len(enum_name) > 0 and get_enum_name(enum_name) != "ENUM": 1975 | enum[member_name] = "MP_ROM_INT(%s)" % member.name 1976 | else: 1977 | int_constants.append(member.name) 1978 | if len(enum) > 0: 1979 | if len(get_enum_name(enum_name)) > 0: 1980 | prev_enum = enums.get(enum_name) 1981 | if prev_enum: 1982 | prev_enum.update(enum) 1983 | else: 1984 | enums[enum_name] = enum 1985 | 1986 | for enum in [ 1987 | enum for enum in enums if len(enums[enum]) == 1 and enum.startswith("ENUM") 1988 | ]: 1989 | int_constants.append("%s_%s" % (enum, next(iter(enums[enum])))) 1990 | del enums[enum] 1991 | 1992 | # Add special string enums 1993 | 1994 | print( 1995 | """ 1996 | /* 1997 | * LVGL string constants 1998 | */ 1999 | """ 2000 | ) 2001 | 2002 | for enum_def in enum_defs: 2003 | if not enum_def.type.values: 2004 | continue 2005 | member_names = [ 2006 | str_enum_to_str(member.name) 2007 | for member in enum_def.type.values.enumerators 2008 | if lv_str_enum_pattern.match(member.name) 2009 | ] 2010 | enum_name = commonprefix(member_names) 2011 | enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix 2012 | enum = collections.OrderedDict() 2013 | if enum_name: 2014 | for member in enum_def.type.values.enumerators: 2015 | full_name = str_enum_to_str(member.name) 2016 | member_name = full_name[len(enum_name) + 1 :] 2017 | print("MP_DEFINE_STR_OBJ(mp_%s, %s);" % (full_name, full_name)) 2018 | enum[member_name] = "&mp_%s" % full_name 2019 | if len(enum) > 0: 2020 | if enum_name in enums: 2021 | enums[enum_name].update(enum) 2022 | else: 2023 | enums[enum_name] = enum 2024 | 2025 | 2026 | # eprint('--> enums: \n%s' % enums) 2027 | 2028 | 2029 | # eprint(',\n'.join(sorted('%s : %s' % (name, get_type(blobs[name])) for name in blobs))) 2030 | 2031 | # 2032 | # Callbacks helper functions 2033 | # 2034 | 2035 | 2036 | def decl_to_callback(decl): 2037 | # print('/* decl_to_callback %s */' % decl) 2038 | if not hasattr(decl, "type"): 2039 | return None 2040 | if isinstance(decl.type, c_ast.PtrDecl) and isinstance( 2041 | decl.type.type, c_ast.FuncDecl 2042 | ): 2043 | return (decl.name, decl.type.type) 2044 | # print('/* callback: ADDED CALLBACK: %s\n%s */' % (gen.visit(decl.type.type), decl.type.type)) 2045 | elif isinstance(decl.type, c_ast.FuncDecl): 2046 | return (decl.name, decl.type) 2047 | # print('/* callback: ADDED CALLBACK: %s\n%s */' % (gen.visit(decl.type.type), decl.type.type)) 2048 | elif isinstance(decl.type, c_ast.TypeDecl) and hasattr(decl.type.type, "names"): 2049 | func_typedef_name = decl.type.type.names[0] 2050 | while func_typedef_name in synonym: 2051 | # eprint('decl_to_callback: %s --> %s' % (func_typedef_name, synonym[func_typedef_name])) 2052 | func_typedef_name = synonym[func_typedef_name] 2053 | # print('/* --> callback: TYPEDEF CALLBACK: %s: %s */' % (decl.name if hasattr(decl, 'name') else None, func_typedef_name)) 2054 | if func_typedef_name in func_typedefs: 2055 | return (decl.name, func_typedefs[func_typedef_name].type.type) 2056 | # print('/* callback: ADDED CALLBACK: %s\n%s */' % (func_typedef_name, func_typedefs[func_typedef_name])) 2057 | else: 2058 | return None 2059 | 2060 | 2061 | def get_user_data_accessors(containing_struct, containing_struct_name=None): 2062 | if not containing_struct_name and containing_struct and containing_struct.name: 2063 | containing_struct_name = containing_struct.name 2064 | if not containing_struct_name: 2065 | return None, None 2066 | base_struct_name = get_base_struct_name(containing_struct_name) 2067 | getter_name = base_struct_name + "_get_user_data" 2068 | setter_name = base_struct_name + "_set_user_data" 2069 | # print('/* struct functions = %s */' % [s.name + ':' + str(len(s.type.args.params)) for s in get_struct_functions(containing_struct_name)]) 2070 | # print('/* getter_name = %s */' % getter_name) 2071 | struct_functions = get_struct_functions(containing_struct_name) 2072 | getters = [ 2073 | s 2074 | for s in struct_functions 2075 | if s.name == getter_name and len(s.type.args.params) == 1 2076 | ] 2077 | setters = [ 2078 | s 2079 | for s in struct_functions 2080 | if s.name == setter_name and len(s.type.args.params) == 2 2081 | ] 2082 | if getters and setters: 2083 | return getters[0], setters[0] 2084 | else: 2085 | return None, None 2086 | 2087 | 2088 | def get_user_data( 2089 | func, func_name=None, containing_struct=None, containing_struct_name=None 2090 | ): 2091 | args = func.args.params 2092 | if not func_name: 2093 | func_name = get_arg_name(func.type) 2094 | # print('/* --> callback: func_name = %s, args = %s */' % (func_name, repr(args))) 2095 | user_data_found = False 2096 | user_data = "None" 2097 | if len(args) > 0 and isinstance(args[0].type, c_ast.PtrDecl): 2098 | # if isinstance(args[0].type.type.type, c_ast.Struct): 2099 | # struct_arg_type_name = args[0].type.type.type.name # PtrDecl.TypeDecl.Struct. Needed to omit 'struct' keyword. 2100 | # else: 2101 | # struct_arg_type_name = get_type(args[0].type.type, remove_quals = True) 2102 | struct_arg_type_name = get_type(args[0].type.type, remove_quals=True) 2103 | # print('/* --> get_user_data: containing_struct_name = %s, struct_arg_type_name = %s */' % (containing_struct_name, struct_arg_type_name)) 2104 | if containing_struct_name and struct_arg_type_name != containing_struct_name: 2105 | return None, None, None 2106 | if not containing_struct: 2107 | try_generate_type(args[0].type) 2108 | if struct_arg_type_name in structs: 2109 | containing_struct = structs[struct_arg_type_name] 2110 | # print('/* --> containing_struct = %s */' % containing_struct) 2111 | # if struct_arg_type_name in mp_to_lv: 2112 | # print('/* --> callback: %s First argument is %s */' % (gen.visit(func), struct_arg_type_name)) 2113 | if containing_struct: 2114 | flatten_struct_decls = flatten_struct(containing_struct.decls) 2115 | user_data = "user_data" 2116 | user_data_found = user_data in [decl.name for decl in flatten_struct_decls] 2117 | # print('/* --> callback: user_data=%s user_data_found=%s containing_struct=%s */' % (user_data, user_data_found, containing_struct)) 2118 | return (user_data if user_data_found else None), *get_user_data_accessors( 2119 | containing_struct, containing_struct_name 2120 | ) 2121 | 2122 | 2123 | # 2124 | # Generate structs when needed 2125 | # 2126 | 2127 | generated_structs = collections.OrderedDict() 2128 | generated_struct_functions = collections.OrderedDict() 2129 | struct_aliases = collections.OrderedDict() 2130 | callbacks_used_on_structs = [] 2131 | 2132 | 2133 | def flatten_struct(struct_decls): 2134 | result = [] 2135 | if not struct_decls: 2136 | return result 2137 | for decl in struct_decls: 2138 | if is_struct(decl.type): 2139 | result.extend(flatten_struct(decl.type.decls)) 2140 | else: 2141 | result.append(decl) 2142 | return result 2143 | 2144 | 2145 | def try_generate_struct(struct_name, struct): 2146 | global lv_to_mp 2147 | global mp_to_lv 2148 | if struct_name in generated_structs: 2149 | return None 2150 | sanitized_struct_name = sanitize(struct_name) 2151 | generated_structs[struct_name] = False # Starting generating a struct 2152 | # print("/* Starting generating %s */" % struct_name) 2153 | if struct_name in mp_to_lv: 2154 | return mp_to_lv[struct_name] 2155 | # print('/* --> try_generate_struct %s: %s\n%s */' % (struct_name, gen.visit(struct), struct)) 2156 | if struct.decls is None: 2157 | if struct_name == struct.name: 2158 | return None 2159 | if struct.name not in structs: 2160 | return None 2161 | return try_generate_type(structs[struct.name]) 2162 | flatten_struct_decls = flatten_struct(struct.decls) 2163 | # Go over fields and try to generate type convertors for each 2164 | # print('!! %s' % struct) 2165 | # print('!!! %s' % flatten_struct_decls) 2166 | write_cases = [] 2167 | read_cases = [] 2168 | for decl in flatten_struct_decls: 2169 | # print('/* ==> decl %s: %s */' % (gen.visit(decl), decl)) 2170 | converted = try_generate_type(decl.type) 2171 | type_name = get_type(decl.type, remove_quals=True) 2172 | # print('/* --> %s: %s (%s)*/' % (decl.name, type_name, mp_to_lv[type_name] if type_name in mp_to_lv else '---')) 2173 | # Handle the case of nested struct 2174 | if not converted and is_struct(decl.type.type): 2175 | parent_name = struct_name 2176 | child_name = decl.type.declname 2177 | type_name = "%s_%s_t" % (parent_name[:-2], child_name) 2178 | print( 2179 | "typedef __typeof__( (({parent}*)(0))->{child} ) {new_struct};".format( 2180 | parent=parent_name, child=child_name, new_struct=type_name 2181 | ) 2182 | ) 2183 | try_generate_struct(type_name, decl.type.type) 2184 | 2185 | # print('==> %s %s: %s' % (type_name, str(type_name in mp_to_lv), decl)) 2186 | 2187 | if (type_name not in mp_to_lv or not mp_to_lv[type_name]) or ( 2188 | type_name not in lv_to_mp or not lv_to_mp[type_name] 2189 | ): 2190 | # eprint("[%s] %s or %s : %s" % (isinstance(decl.type,c_ast.PtrDecl), type_name, get_type(decl.type), decl.type)) 2191 | if type_name in generated_structs: 2192 | print( 2193 | "/* Already started generating %s! skipping field '%s' */" 2194 | % (type_name, decl.name) 2195 | ) 2196 | continue 2197 | raise MissingConversionException( 2198 | "Missing conversion to %s when generating struct %s.%s" 2199 | % (type_name, struct_name, get_name(decl)) 2200 | ) 2201 | 2202 | mp_to_lv_convertor = mp_to_lv[type_name] 2203 | lv_to_mp_convertor = ( 2204 | lv_to_mp_byref[type_name] 2205 | if type_name in lv_to_mp_byref 2206 | else lv_to_mp[type_name] 2207 | ) 2208 | 2209 | cast = ( 2210 | "(void*)" if isinstance(decl.type, c_ast.PtrDecl) else "" 2211 | ) # needed when field is const. casting to void overrides it 2212 | 2213 | callback = decl_to_callback(decl) 2214 | 2215 | if callback: 2216 | # print("/* %s callback %s */" % (gen.visit(decl), callback)) 2217 | func_name, arg_type = callback 2218 | user_data, _, _ = get_user_data( 2219 | arg_type, 2220 | func_name=func_name, 2221 | containing_struct=struct, 2222 | containing_struct_name=struct_name, 2223 | ) 2224 | if callback not in callbacks_used_on_structs: 2225 | callbacks_used_on_structs.append(callback + (struct_name,)) 2226 | # Emit callback forward decl. 2227 | if user_data in [ 2228 | user_data_decl.name for user_data_decl in flatten_struct_decls 2229 | ]: 2230 | full_user_data = "data->%s" % user_data 2231 | full_user_data_ptr = "&%s" % full_user_data 2232 | lv_callback = "%s_%s_callback" % (struct_name, func_name) 2233 | print( 2234 | "static %s %s_%s_callback(%s);" 2235 | % ( 2236 | get_type(arg_type.type, remove_quals=False), 2237 | struct_name, 2238 | func_name, 2239 | gen.visit(arg_type.args), 2240 | ) 2241 | ) 2242 | else: 2243 | full_user_data = "NULL" 2244 | full_user_data_ptr = full_user_data 2245 | lv_callback = "NULL" 2246 | if not user_data: 2247 | gen_func_error( 2248 | decl, 2249 | "Missing 'user_data' as a field of the first parameter of the callback function '%s_%s_callback'" 2250 | % (struct_name, func_name), 2251 | ) 2252 | else: 2253 | gen_func_error( 2254 | decl, "Missing 'user_data' member in struct '%s'" % struct_name 2255 | ) 2256 | write_cases.append( 2257 | "case MP_QSTR_{field}: data->{decl_name} = {cast}mp_lv_callback(dest[1], {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}, NULL, NULL, NULL); break; // converting to callback {type_name}".format( 2258 | struct_name=struct_name, 2259 | field=sanitize(decl.name), 2260 | decl_name=decl.name, 2261 | lv_callback=lv_callback, 2262 | user_data=full_user_data_ptr, 2263 | type_name=type_name, 2264 | cast=cast, 2265 | ) 2266 | ) 2267 | read_cases.append( 2268 | "case MP_QSTR_{field}: dest[0] = mp_lv_funcptr(&mp_{funcptr}_mpobj, {cast}data->{decl_name}, {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}); break; // converting from callback {type_name}".format( 2269 | struct_name=struct_name, 2270 | field=sanitize(decl.name), 2271 | decl_name=decl.name, 2272 | lv_callback=lv_callback, 2273 | funcptr=lv_to_mp_funcptr[type_name], 2274 | user_data=full_user_data, 2275 | type_name=type_name, 2276 | cast=cast, 2277 | ) 2278 | ) 2279 | else: 2280 | user_data = None 2281 | # Only allow write to non-const members 2282 | is_writeable = ( 2283 | not hasattr(decl.type, "quals") 2284 | ) or "const" not in decl.type.quals 2285 | # Arrays must be handled by memcpy, otherwise we would get "assignment to expression with array type" error 2286 | if isinstance(decl.type, c_ast.ArrayDecl): 2287 | memcpy_size = "sizeof(%s)*%s" % ( 2288 | gen.visit(decl.type.type), 2289 | gen.visit(decl.type.dim), 2290 | ) 2291 | if is_writeable: 2292 | write_cases.append( 2293 | "case MP_QSTR_{field}: memcpy((void*)&data->{decl_name}, {cast}{convertor}(dest[1]), {size}); break; // converting to {type_name}".format( 2294 | field=sanitize(decl.name), 2295 | decl_name=decl.name, 2296 | convertor=mp_to_lv_convertor, 2297 | type_name=type_name, 2298 | cast=cast, 2299 | size=memcpy_size, 2300 | ) 2301 | ) 2302 | read_cases.append( 2303 | "case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}".format( 2304 | field=sanitize(decl.name), 2305 | decl_name=decl.name, 2306 | convertor=lv_to_mp_convertor, 2307 | type_name=type_name, 2308 | cast=cast, 2309 | ) 2310 | ) 2311 | else: 2312 | if is_writeable: 2313 | write_cases.append( 2314 | "case MP_QSTR_{field}: data->{decl_name} = {cast}{convertor}(dest[1]); break; // converting to {type_name}".format( 2315 | field=sanitize(decl.name), 2316 | decl_name=decl.name, 2317 | convertor=mp_to_lv_convertor, 2318 | type_name=type_name, 2319 | cast=cast, 2320 | ) 2321 | ) 2322 | read_cases.append( 2323 | "case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}".format( 2324 | field=sanitize(decl.name), 2325 | decl_name=decl.name, 2326 | convertor=lv_to_mp_convertor, 2327 | type_name=type_name, 2328 | cast=cast, 2329 | ) 2330 | ) 2331 | print( 2332 | """ 2333 | /* 2334 | * Struct {struct_name} 2335 | */ 2336 | 2337 | static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type(); 2338 | 2339 | static inline void* mp_write_ptr_{sanitized_struct_name}(mp_obj_t self_in) 2340 | {{ 2341 | mp_lv_struct_t *self = MP_OBJ_TO_PTR(cast(self_in, get_mp_{sanitized_struct_name}_type())); 2342 | return ({struct_tag}{struct_name}*)self->data; 2343 | }} 2344 | 2345 | #define mp_write_{sanitized_struct_name}(struct_obj) *(({struct_tag}{struct_name}*)mp_write_ptr_{sanitized_struct_name}(struct_obj)) 2346 | 2347 | static inline mp_obj_t mp_read_ptr_{sanitized_struct_name}(void *field) 2348 | {{ 2349 | return lv_to_mp_struct(get_mp_{sanitized_struct_name}_type(), field); 2350 | }} 2351 | 2352 | #define mp_read_{sanitized_struct_name}(field) mp_read_ptr_{sanitized_struct_name}(copy_buffer(&field, sizeof({struct_tag}{struct_name}))) 2353 | #define mp_read_byref_{sanitized_struct_name}(field) mp_read_ptr_{sanitized_struct_name}(&field) 2354 | 2355 | static void mp_{sanitized_struct_name}_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) 2356 | {{ 2357 | mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); 2358 | GENMPY_UNUSED {struct_tag}{struct_name} *data = ({struct_tag}{struct_name}*)self->data; 2359 | 2360 | if (dest[0] == MP_OBJ_NULL) {{ 2361 | // load attribute 2362 | switch(attr) 2363 | {{ 2364 | {read_cases}; 2365 | default: call_parent_methods(self_in, attr, dest); // fallback to locals_dict lookup 2366 | }} 2367 | }} else {{ 2368 | if (dest[1]) 2369 | {{ 2370 | // store attribute 2371 | switch(attr) 2372 | {{ 2373 | {write_cases}; 2374 | default: return; 2375 | }} 2376 | 2377 | dest[0] = MP_OBJ_NULL; // indicate success 2378 | }} 2379 | }} 2380 | }} 2381 | 2382 | static void mp_{sanitized_struct_name}_print(const mp_print_t *print, 2383 | mp_obj_t self_in, 2384 | mp_print_kind_t kind) 2385 | {{ 2386 | mp_printf(print, "struct {struct_name}"); 2387 | }} 2388 | 2389 | static const mp_obj_dict_t mp_{sanitized_struct_name}_locals_dict; 2390 | 2391 | static MP_DEFINE_CONST_OBJ_TYPE( 2392 | mp_{sanitized_struct_name}_type, 2393 | MP_QSTR_{sanitized_struct_name}, 2394 | MP_TYPE_FLAG_NONE, 2395 | print, mp_{sanitized_struct_name}_print, 2396 | make_new, make_new_lv_struct, 2397 | binary_op, lv_struct_binary_op, 2398 | subscr, lv_struct_subscr, 2399 | attr, mp_{sanitized_struct_name}_attr, 2400 | locals_dict, &mp_{sanitized_struct_name}_locals_dict, 2401 | buffer, mp_blob_get_buffer, 2402 | parent, &mp_lv_base_struct_type 2403 | ); 2404 | 2405 | static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type() 2406 | {{ 2407 | return &mp_{sanitized_struct_name}_type; 2408 | }} 2409 | """.format( 2410 | sanitized_struct_name=sanitized_struct_name, 2411 | struct_name=struct_name, 2412 | struct_tag="struct " 2413 | if struct_name in structs_without_typedef.keys() 2414 | else "", 2415 | write_cases=";\n ".join(write_cases), 2416 | read_cases=";\n ".join(read_cases), 2417 | ) 2418 | ) 2419 | lv_to_mp[struct_name] = "mp_read_%s" % sanitized_struct_name 2420 | lv_to_mp_byref[struct_name] = "mp_read_byref_%s" % sanitized_struct_name 2421 | mp_to_lv[struct_name] = "mp_write_%s" % sanitized_struct_name 2422 | lv_to_mp["%s *" % struct_name] = "mp_read_ptr_%s" % sanitized_struct_name 2423 | mp_to_lv["%s *" % struct_name] = "mp_write_ptr_%s" % sanitized_struct_name 2424 | lv_to_mp["const %s *" % struct_name] = "mp_read_ptr_%s" % sanitized_struct_name 2425 | mp_to_lv["const %s *" % struct_name] = "mp_write_ptr_%s" % sanitized_struct_name 2426 | lv_mp_type[struct_name] = simplify_identifier(sanitized_struct_name) 2427 | lv_mp_type["%s *" % struct_name] = simplify_identifier(sanitized_struct_name) 2428 | lv_mp_type["const %s *" % struct_name] = simplify_identifier(sanitized_struct_name) 2429 | # print('/* --> struct "%s" generated! */' % (struct_name)) 2430 | generated_structs[struct_name] = True # Completed generating a struct 2431 | return struct_name 2432 | 2433 | 2434 | # 2435 | # Generate Array Types when needed 2436 | # 2437 | 2438 | 2439 | def try_generate_array_type(type_ast): 2440 | arr_name = get_name(type_ast) 2441 | if arr_name in mp_to_lv: 2442 | return mp_to_lv[arr_name] 2443 | # print('/* --> try_generate_array_type %s: %s */' % (arr_name, type_ast)) 2444 | dim = gen.visit(type_ast.dim) if hasattr(type_ast, "dim") and type_ast.dim else None 2445 | element_type = get_type(type_ast.type, remove_quals=True) 2446 | qualified_element_type = gen.visit(type_ast.type) 2447 | if element_type not in mp_to_lv or not mp_to_lv[element_type]: 2448 | try_generate_type(type_ast.type) 2449 | if element_type not in mp_to_lv or not mp_to_lv[element_type]: 2450 | raise MissingConversionException( 2451 | "Missing conversion to %s while generating array type conversion" 2452 | % element_type 2453 | ) 2454 | type_ptr_ast = c_ast.PtrDecl(quals=[], type=type_ast.type) 2455 | element_type_ptr = get_type(type_ptr_ast, remove_quals=True) 2456 | qualified_element_ptr_type = gen.visit(type_ptr_ast) 2457 | if element_type_ptr not in mp_to_lv or not mp_to_lv[element_type_ptr]: 2458 | try_generate_type(type_ptr_ast) 2459 | if element_type_ptr not in mp_to_lv or not mp_to_lv[element_type_ptr]: 2460 | raise MissingConversionException( 2461 | "Missing conversion to %s while generating array type conversion" 2462 | % element_type_ptr 2463 | ) 2464 | array_convertor_suffix = ( 2465 | arr_name.replace(" ", "_") 2466 | .replace("*", "ptr") 2467 | .replace("+", "plus") 2468 | .replace("-", "minus") 2469 | .replace("[", "__") 2470 | .replace("]", "__") 2471 | .replace("(", "__") 2472 | .replace(")", "__") 2473 | .replace("/", "_div_") 2474 | ) 2475 | arr_to_c_convertor_name = "mp_arr_to_%s" % array_convertor_suffix 2476 | arr_to_mp_convertor_name = "mp_arr_from_%s" % array_convertor_suffix 2477 | print( 2478 | ( 2479 | ( 2480 | """ 2481 | /* 2482 | * Array convertors for {arr_name} 2483 | */ 2484 | 2485 | GENMPY_UNUSED static {struct_tag}{type} *{arr_to_c_convertor_name}(mp_obj_t mp_arr) 2486 | {{ 2487 | mp_obj_t mp_len = mp_obj_len_maybe(mp_arr); 2488 | if (mp_len == MP_OBJ_NULL) return mp_to_ptr(mp_arr); 2489 | mp_int_t len = mp_obj_get_int(mp_len); 2490 | {check_dim} 2491 | {struct_tag}{type} *lv_arr = ({struct_tag}{type}*)m_malloc(len * sizeof({struct_tag}{type})); 2492 | mp_obj_t iter = mp_getiter(mp_arr, NULL); 2493 | mp_obj_t item; 2494 | size_t i = 0; 2495 | while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {{ 2496 | lv_arr[i++] = {mp_to_lv_convertor}(item); 2497 | }} 2498 | return ({struct_tag}{type} *)lv_arr; 2499 | }} 2500 | """ 2501 | ) 2502 | + ( 2503 | """ 2504 | GENMPY_UNUSED static mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) 2505 | {{ 2506 | mp_obj_t obj_arr[{dim}]; 2507 | for (size_t i=0; i<{dim}; i++){{ 2508 | obj_arr[i] = {lv_to_mp_convertor}(arr[i]); 2509 | }} 2510 | return mp_obj_new_list({dim}, obj_arr); // TODO: return custom iterable object! 2511 | }} 2512 | """ 2513 | if dim 2514 | else """ 2515 | GENMPY_UNUSED static mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) 2516 | {{ 2517 | return {lv_to_mp_ptr_convertor}((void*)arr); 2518 | }} 2519 | """ 2520 | ) 2521 | ).format( 2522 | arr_to_c_convertor_name=arr_to_c_convertor_name, 2523 | arr_to_mp_convertor_name=arr_to_mp_convertor_name, 2524 | arr_name=arr_name, 2525 | type=element_type, 2526 | type_ptr=element_type_ptr, 2527 | struct_tag="struct " 2528 | if element_type in structs_without_typedef.keys() 2529 | else "", 2530 | qualified_type=qualified_element_type, 2531 | qualified_ptr_type=qualified_element_ptr_type, 2532 | check_dim="//TODO check dim!" if dim else "", 2533 | mp_to_lv_convertor=mp_to_lv[element_type], 2534 | lv_to_mp_convertor=lv_to_mp[element_type], 2535 | mp_to_lv_ptr_convertor=mp_to_lv[element_type_ptr], 2536 | lv_to_mp_ptr_convertor=lv_to_mp[element_type_ptr], 2537 | dim=dim if dim else 1, 2538 | ) 2539 | ) 2540 | mp_to_lv[arr_name] = arr_to_c_convertor_name 2541 | mp_to_lv["const %s" % arr_name] = arr_to_c_convertor_name 2542 | lv_to_mp[arr_name] = arr_to_mp_convertor_name 2543 | lv_to_mp["const %s" % arr_name] = arr_to_mp_convertor_name 2544 | lv_mp_type[arr_name] = arr_to_c_convertor_name 2545 | lv_mp_type["const %s" % arr_name] = "const %s" % arr_to_c_convertor_name 2546 | return arr_to_c_convertor_name 2547 | 2548 | 2549 | # 2550 | # Generate types from typedefs when needed 2551 | # 2552 | 2553 | 2554 | def get_arg_name(arg): 2555 | if isinstance(arg, c_ast.PtrDecl) or isinstance(arg, c_ast.FuncDecl): 2556 | return get_arg_name(arg.type) 2557 | if hasattr(arg, "declname"): 2558 | return arg.declname 2559 | if hasattr(arg, "name"): 2560 | return arg.name 2561 | return "unnamed_arg" 2562 | 2563 | 2564 | # print("// Typedefs: " + ", ".join(get_arg_name(t) for t in typedefs)) 2565 | 2566 | 2567 | def try_generate_type(type_ast): 2568 | # eprint(' --> try_generate_type %s : %s' % (get_name(type_ast), gen.visit(type_ast))) 2569 | # print('/* --> try_generate_type %s: %s */' % (get_name(type_ast), type_ast)) 2570 | if isinstance(type_ast, str): 2571 | raise SyntaxError("Internal error! try_generate_type argument is a string.") 2572 | # Handle the case of a pointer 2573 | if isinstance(type_ast, c_ast.TypeDecl): 2574 | return try_generate_type(type_ast.type) 2575 | type = get_name(type_ast) 2576 | if isinstance(type_ast, c_ast.Enum): 2577 | mp_to_lv[type] = mp_to_lv["int"] 2578 | mp_to_lv["%s *" % type] = mp_to_lv["int *"] 2579 | lv_to_mp[type] = lv_to_mp["int"] 2580 | lv_to_mp["%s *" % type] = lv_to_mp["int *"] 2581 | lv_mp_type[type] = lv_mp_type["int"] 2582 | lv_mp_type["%s *" % type] = lv_mp_type["int *"] 2583 | return mp_to_lv[type] 2584 | if type in mp_to_lv: 2585 | return mp_to_lv[type] 2586 | if isinstance(type_ast, c_ast.ArrayDecl) and try_generate_array_type(type_ast): 2587 | return mp_to_lv[type] 2588 | if isinstance(type_ast, (c_ast.PtrDecl, c_ast.ArrayDecl)): 2589 | type = get_name(type_ast.type.type) 2590 | ptr_type = get_type(type_ast, remove_quals=True) 2591 | # print('/* --> try_generate_type IS PtrDecl!! %s: %s */' % (type, type_ast)) 2592 | if type in structs: 2593 | try_generate_struct(type, structs[type]) if type in structs else None 2594 | if ( 2595 | isinstance(type_ast.type, c_ast.TypeDecl) 2596 | and isinstance(type_ast.type.type, c_ast.Struct) 2597 | and (type_ast.type.type.name in structs) 2598 | ): 2599 | try_generate_struct(type, structs[type_ast.type.type.name]) 2600 | if isinstance(type_ast.type, c_ast.FuncDecl): 2601 | if isinstance(type_ast.type.type.type, c_ast.TypeDecl): 2602 | type = type_ast.type.type.type.declname 2603 | func_ptr_name = "funcptr_%s" % type 2604 | 2605 | i = 1 2606 | while func_ptr_name in generated_funcs: # Make sure func_ptr_name is unique 2607 | func_ptr_name = "funcptr_%s_%d" % (type, i) 2608 | i += 1 2609 | 2610 | func = c_ast.Decl( 2611 | name=func_ptr_name, 2612 | quals=[], 2613 | align=[], 2614 | storage=[], 2615 | funcspec=[], 2616 | type=type_ast.type, 2617 | init=None, 2618 | bitsize=None, 2619 | ) 2620 | try: 2621 | print("#define %s NULL\n" % func_ptr_name) 2622 | gen_mp_func(func, None) 2623 | print( 2624 | "static inline mp_obj_t mp_lv_{f}(void *func){{ return mp_lv_funcptr(&mp_{f}_mpobj, func, NULL, MP_QSTR_, NULL); }}\n".format( 2625 | f=func_ptr_name 2626 | ) 2627 | ) 2628 | lv_to_mp_funcptr[ptr_type] = func_ptr_name 2629 | # eprint("/* --> lv_to_mp_funcptr[%s] = %s */" % (ptr_type, func_ptr_name)) 2630 | lv_to_mp[ptr_type] = "mp_lv_%s" % func_ptr_name 2631 | lv_mp_type[ptr_type] = "function pointer" 2632 | except MissingConversionException as exp: 2633 | gen_func_error(func, exp) 2634 | # print('/* --> PTR %s */' % ptr_type) 2635 | if ptr_type not in mp_to_lv: 2636 | mp_to_lv[ptr_type] = mp_to_lv["void *"] 2637 | if ptr_type not in lv_to_mp: 2638 | lv_to_mp[ptr_type] = lv_to_mp["void *"] 2639 | if ptr_type not in lv_mp_type: 2640 | lv_mp_type[ptr_type] = "void*" 2641 | return mp_to_lv[ptr_type] 2642 | if type in structs: 2643 | if try_generate_struct(type, structs[type]): 2644 | return mp_to_lv[type] 2645 | for new_type_ast in [x for x in typedefs if get_arg_name(x) == type]: 2646 | new_type = get_type(new_type_ast, remove_quals=True) 2647 | if ( 2648 | isinstance(new_type_ast, c_ast.TypeDecl) 2649 | and isinstance(new_type_ast.type, c_ast.Struct) 2650 | and not new_type_ast.type.decls 2651 | ): 2652 | explicit_struct_name = ( 2653 | new_type_ast.type.name 2654 | if hasattr(new_type_ast.type, "name") 2655 | else new_type_ast.type.names[0] 2656 | ) 2657 | else: 2658 | explicit_struct_name = new_type 2659 | if type == explicit_struct_name: 2660 | continue 2661 | # eprint('/* --> typedef: %s --> %s (%s) */' % (type, new_type, new_type_ast)) 2662 | if explicit_struct_name in structs: 2663 | if try_generate_struct(new_type, structs[explicit_struct_name]): 2664 | if explicit_struct_name == new_type: 2665 | struct_aliases[new_type] = type 2666 | if type != new_type and try_generate_type(new_type_ast): 2667 | # eprint('/* --> try_generate_type TYPEDEF!! %s: %s */' % (type, mp_to_lv[new_type])) 2668 | mp_to_lv[type] = mp_to_lv[new_type] 2669 | type_ptr = "%s *" % type 2670 | new_type_ptr = "%s *" % new_type 2671 | if new_type_ptr in mp_to_lv: 2672 | mp_to_lv[type_ptr] = mp_to_lv[new_type_ptr] 2673 | if new_type in lv_to_mp: 2674 | lv_to_mp[type] = lv_to_mp[new_type] 2675 | lv_mp_type[type] = lv_mp_type[new_type] 2676 | if new_type in lv_to_mp_funcptr: 2677 | lv_to_mp_funcptr[type] = lv_to_mp_funcptr[new_type] 2678 | if new_type in lv_to_mp_byref: 2679 | lv_to_mp_byref[type] = lv_to_mp_byref[new_type] 2680 | if new_type_ptr in lv_to_mp: 2681 | lv_to_mp[type_ptr] = lv_to_mp[new_type_ptr] 2682 | if new_type_ptr in lv_mp_type: 2683 | lv_mp_type[type_ptr] = lv_mp_type[new_type_ptr] 2684 | # eprint('/* --> %s = (%s) */' % (type, new_type)) 2685 | return mp_to_lv[type] 2686 | return None 2687 | 2688 | 2689 | # 2690 | # Helper structs 2691 | # 2692 | 2693 | 2694 | def create_helper_struct(struct_str): 2695 | print(struct_str) 2696 | struct_str_ast = parser.parse(struct_str).ext[0].type 2697 | struct_name = get_name(struct_str_ast) 2698 | # print('/* --> %s: %s */' % (struct_name, struct_str_ast.type)) 2699 | structs[struct_name] = struct_str_ast.type 2700 | try: 2701 | try_generate_struct(struct_name, struct_str_ast.type) 2702 | except MissingConversionException as exp: 2703 | print("/* Helper structs NOT generated:\n %s\n*/" % (repr(exp))) 2704 | 2705 | 2706 | print( 2707 | """ 2708 | /* 2709 | * Helper Structs 2710 | */ 2711 | """ 2712 | ) 2713 | 2714 | create_helper_struct( 2715 | """ 2716 | typedef union { 2717 | void* ptr_val; 2718 | const char* str_val; 2719 | int int_val; 2720 | unsigned int uint_val; 2721 | } C_Pointer; 2722 | """ 2723 | ) 2724 | 2725 | # 2726 | # Emit C callback functions 2727 | # 2728 | 2729 | generated_callbacks = collections.OrderedDict() 2730 | 2731 | 2732 | def build_callback_func_arg(arg, index, func, func_name=None): 2733 | arg_type = get_type(arg.type, remove_quals=True) 2734 | cast = ( 2735 | "(void*)" if isinstance(arg.type, c_ast.PtrDecl) else "" 2736 | ) # needed when field is const. casting to void overrides it 2737 | if arg_type not in lv_to_mp or not lv_to_mp[arg_type]: 2738 | try_generate_type(arg.type) 2739 | if arg_type not in lv_to_mp or not lv_to_mp[arg_type]: 2740 | raise MissingConversionException( 2741 | "Callback: Missing conversion to %s" % arg_type 2742 | ) 2743 | arg_metadata = {"type": lv_mp_type[arg_type]} 2744 | if arg.name: 2745 | arg_metadata["name"] = arg.name 2746 | callback_metadata[func_name]["args"].append(arg_metadata) 2747 | return "mp_args[{i}] = {convertor}({cast}arg{i});".format( 2748 | convertor=lv_to_mp[arg_type], i=index, cast=cast 2749 | ) 2750 | 2751 | 2752 | def gen_callback_func(func, func_name=None, user_data_argument=False): 2753 | global mp_to_lv 2754 | if func_name in generated_callbacks: 2755 | return 2756 | # print('/* --> callback: %s */' % (gen.visit(func))) 2757 | callback_metadata[func_name] = {"args": []} 2758 | args = func.args.params 2759 | enumerated_args = [] 2760 | for i, arg in enumerate(args): 2761 | arg_name = f"arg{i}" 2762 | new_arg = c_ast.Decl( 2763 | name=arg_name, 2764 | quals=arg.quals, 2765 | align=[], 2766 | storage=[], 2767 | funcspec=[], 2768 | type=copy.deepcopy(arg.type), 2769 | init=None, 2770 | bitsize=None, 2771 | ) 2772 | t = new_arg 2773 | while hasattr(t, "type"): 2774 | if hasattr(t.type, "declname"): 2775 | t.type.declname = arg_name 2776 | t = t.type 2777 | enumerated_args.append(new_arg) 2778 | # print(f'/* --> {gen.visit(new_arg)} */') 2779 | if not func_name: 2780 | func_name = get_arg_name(func.type) 2781 | # print('/* --> callback: func_name = %s */' % func_name) 2782 | if is_global_callback(func): 2783 | full_user_data = "MP_STATE_PORT(mp_lv_user_data)" 2784 | else: 2785 | user_data, user_data_getter, _ = get_user_data(func, func_name) 2786 | 2787 | if ( 2788 | user_data_argument 2789 | and len(args) > 0 2790 | and gen.visit(args[-1].type) == "void *" 2791 | ): 2792 | full_user_data = "arg%d" % (len(args) - 1) 2793 | elif user_data: 2794 | full_user_data = "arg0->%s" % user_data 2795 | if ( 2796 | len(args) < 1 2797 | or hasattr(args[0].type.type, "names") 2798 | and lv_base_obj_pattern.match(args[0].type.type.names[0]) 2799 | ): 2800 | raise MissingConversionException( 2801 | "Callback: First argument of callback function must be lv_obj_t" 2802 | ) 2803 | elif user_data_getter: 2804 | full_user_data = "%s(arg0)" % user_data_getter.name 2805 | else: 2806 | full_user_data = None 2807 | 2808 | # if full_user_data: print('/* --> callback: %s user_data found!! %s */' %(gen.visit(func), full_user_data)) 2809 | # else: print('/* --> callback: full_user_data NOT FOUND !! %s */' % (gen.visit(func))) 2810 | if not full_user_data: 2811 | raise MissingConversionException( 2812 | "Callback: user_data NOT FOUND! %s" % (gen.visit(func)) 2813 | ) 2814 | return_type = get_type(func.type, remove_quals=False) 2815 | if return_type != "void" and ( 2816 | return_type not in mp_to_lv or not mp_to_lv[return_type] 2817 | ): 2818 | try_generate_type(func.type) 2819 | if return_type not in mp_to_lv or not mp_to_lv[return_type]: 2820 | raise MissingConversionException( 2821 | "Callback return value: Missing conversion to %s" % return_type 2822 | ) 2823 | 2824 | callback_metadata[func_name]["return_type"] = lv_mp_type[return_type] 2825 | print( 2826 | """ 2827 | /* 2828 | * Callback function {func_name} 2829 | * {func_prototype} 2830 | */ 2831 | 2832 | GENMPY_UNUSED static {return_type} {func_name}_callback({func_args}) 2833 | {{ 2834 | mp_obj_t mp_args[{num_args}]; 2835 | {build_args} 2836 | mp_obj_t callbacks = get_callback_dict_from_user_data({user_data}); 2837 | _nesting++; 2838 | {return_value_assignment}mp_call_function_n_kw(mp_obj_dict_get(callbacks, MP_OBJ_NEW_QSTR(MP_QSTR_{func_name})) , {num_args}, 0, mp_args); 2839 | _nesting--; 2840 | return{return_value}; 2841 | }} 2842 | """.format( 2843 | func_prototype=gen.visit(func), 2844 | func_name=sanitize(func_name), 2845 | return_type=return_type, 2846 | func_args=", ".join([(gen.visit(arg)) for arg in enumerated_args]), 2847 | num_args=len(args), 2848 | build_args="\n ".join( 2849 | [ 2850 | build_callback_func_arg(arg, i, func, func_name=func_name) 2851 | for i, arg in enumerate(args) 2852 | ] 2853 | ), 2854 | user_data=full_user_data, 2855 | return_value_assignment="" 2856 | if return_type == "void" 2857 | else "mp_obj_t callback_result = ", 2858 | return_value="" 2859 | if return_type == "void" 2860 | else " %s(callback_result)" % mp_to_lv[return_type], 2861 | ) 2862 | ) 2863 | generated_callbacks[func_name] = True 2864 | 2865 | 2866 | # 2867 | # Emit Mpy function definitions 2868 | # 2869 | 2870 | generated_funcs = collections.OrderedDict() 2871 | 2872 | 2873 | def build_mp_func_arg(arg, index, func, obj_name): 2874 | if isinstance(arg, c_ast.EllipsisParam): 2875 | raise MissingConversionException("Cannot convert ellipsis param") 2876 | fixed_arg = copy.deepcopy(arg) 2877 | convert_array_to_ptr(fixed_arg) 2878 | if not fixed_arg.name: 2879 | fixed_arg.name = "arg%d" % index 2880 | add_default_declname(fixed_arg, fixed_arg.name) 2881 | # print('/* --> FIXED ARG: %s */' % repr(fixed_arg)) 2882 | callback = decl_to_callback(arg) 2883 | args = func.type.args.params if func.type.args else [] 2884 | # print('/* --> ARG: %s */' % arg) 2885 | # print('/* --> FIRST ARG: %s */' % first_arg) 2886 | if callback: 2887 | # Callback is supported in two modes: 2888 | # 1) last argument is a void* user_data which is passed to callback 2889 | # 2) first argument is a struct with user_data field, which is passed to callback 2890 | 2891 | callback_name, arg_type = callback 2892 | # print('/* --> callback %s ARG TYPE: %s */' % (callback_name, arg_type)) 2893 | 2894 | try: 2895 | user_data_argument = False 2896 | full_user_data = None 2897 | user_data_getter = None 2898 | user_data_setter = None 2899 | if ( 2900 | len(args) > 0 2901 | and gen.visit(args[-1].type) == "void *" 2902 | and args[-1].name == "user_data" 2903 | ): 2904 | callback_name = "%s_%s" % (func.name, callback_name) 2905 | full_user_data = "&user_data" 2906 | user_data_argument = True 2907 | else: 2908 | first_arg = args[0] 2909 | struct_name = get_name( 2910 | first_arg.type.type.type 2911 | if hasattr(first_arg.type.type, "type") 2912 | else first_arg.type.type 2913 | ) 2914 | callback_name = "%s_%s" % (struct_name, callback_name) 2915 | user_data, user_data_getter, user_data_setter = get_user_data( 2916 | arg_type, callback_name 2917 | ) 2918 | if is_global_callback(arg_type): 2919 | full_user_data = "&MP_STATE_PORT(mp_lv_user_data)" 2920 | else: 2921 | if user_data: 2922 | full_user_data = "&%s->%s" % (first_arg.name, user_data) 2923 | elif user_data_getter and user_data_setter: 2924 | full_user_data = "NULL" # uses getter/setter instead 2925 | if index == 0: 2926 | raise MissingConversionException( 2927 | "Callback argument '%s' cannot be the first argument! We assume the first argument contains the user_data" 2928 | % gen.visit(arg) 2929 | ) 2930 | if not full_user_data: 2931 | raise MissingConversionException( 2932 | "Callback function '%s' must receive a struct pointer with user_data member as its first argument!" 2933 | % gen.visit(arg) 2934 | ) 2935 | # eprint("--> callback_metadata= %s_%s" % (struct_name, callback_name)) 2936 | gen_callback_func(arg_type, "%s" % callback_name, user_data_argument) 2937 | arg_metadata = { 2938 | "type": "callback", 2939 | "function": callback_metadata[callback_name], 2940 | } 2941 | if arg.name: 2942 | arg_metadata["name"] = arg.name 2943 | func_metadata[func.name]["args"].append(arg_metadata) 2944 | return "void *{arg_name} = mp_lv_callback(mp_args[{i}], &{callback_name}_callback, MP_QSTR_{callback_name}, {full_user_data}, {containing_struct}, (mp_lv_get_user_data){user_data_getter}, (mp_lv_set_user_data){user_data_setter});".format( 2945 | i=index, 2946 | arg_name=fixed_arg.name, 2947 | callback_name=sanitize(callback_name), 2948 | full_user_data=full_user_data, 2949 | containing_struct=first_arg.name 2950 | if user_data_getter and user_data_setter 2951 | else "NULL", 2952 | user_data_getter=user_data_getter.name if user_data_getter else "NULL", 2953 | user_data_setter=user_data_setter.name if user_data_setter else "NULL", 2954 | ) 2955 | except MissingConversionException as exp: 2956 | gen_func_error(arg, exp) 2957 | callback_name = "NULL" 2958 | full_user_data = "NULL" 2959 | if not hasattr(arg, "type"): 2960 | raise MissingConversionException( 2961 | "Cannot convert function argument %s" % repr(arg) 2962 | ) 2963 | arg_type = get_type(arg.type, remove_quals=True) 2964 | # print('/* --> arg = %s, arg_type = %s */' %(gen.visit(arg), arg_type)) 2965 | if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: 2966 | try_generate_type(arg.type) 2967 | if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: 2968 | raise MissingConversionException("Missing conversion to %s" % arg_type) 2969 | arg_metadata = {"type": lv_mp_type[arg_type]} 2970 | if arg.name: 2971 | arg_metadata["name"] = arg.name 2972 | func_metadata[func.name]["args"].append(arg_metadata) 2973 | cast = ( 2974 | ("(%s)" % gen.visit(fixed_arg.type)) if "const" in arg.quals else "" 2975 | ) # allow conversion from non const to const, sometimes requires cast 2976 | return "{var} = {cast}{convertor}(mp_args[{i}]);".format( 2977 | var=gen.visit(fixed_arg), cast=cast, convertor=mp_to_lv[arg_type], i=index 2978 | ) 2979 | 2980 | 2981 | def emit_func_obj(func_obj_name, func_name, param_count, func_ptr, is_static): 2982 | print( 2983 | """ 2984 | static {builtin_macro}(mp_{func_obj_name}_mpobj, {param_count}, mp_{func_name}, {func_ptr}); 2985 | """.format( 2986 | func_obj_name=func_obj_name, 2987 | func_name=func_name, 2988 | func_ptr=func_ptr, 2989 | param_count=param_count, 2990 | builtin_macro="MP_DEFINE_CONST_LV_FUN_OBJ_STATIC_VAR" 2991 | if is_static 2992 | else "MP_DEFINE_CONST_LV_FUN_OBJ_VAR", 2993 | ) 2994 | ) 2995 | 2996 | 2997 | def gen_mp_func(func, obj_name): 2998 | # print('/* gen_mp_func: %s : %s */' % (obj_name, func)) 2999 | if func.name in generated_funcs: 3000 | print( 3001 | """ 3002 | /* 3003 | * WARNING: %s was declared more than once! 3004 | */ 3005 | """ 3006 | % func.name 3007 | ) 3008 | return 3009 | # print("/* gen_mp_func %s */" % func.name) 3010 | generated_funcs[func.name] = False # starting to generate the function 3011 | func_metadata[func.name] = {"type": "function", "args": []} 3012 | 3013 | args = func.type.args.params if func.type.args else [] 3014 | enumerated_args = enumerate(args) 3015 | 3016 | # Handle the case of a single function argument which is "void" 3017 | if len(args) == 1 and get_type(args[0].type, remove_quals=True) == "void": 3018 | param_count = 0 3019 | args = [] 3020 | else: 3021 | param_count = len(args) 3022 | 3023 | # If func prototype matches an already generated func, reuse it and only emit func obj that points to it. 3024 | prototype_str = gen.visit(function_prototype(func)) 3025 | if prototype_str in func_prototypes: 3026 | original_func = func_prototypes[prototype_str] 3027 | if generated_funcs[original_func.name] == True: 3028 | print("/* Reusing %s for %s */" % (original_func.name, func.name)) 3029 | emit_func_obj( 3030 | func.name, 3031 | original_func.name, 3032 | param_count, 3033 | func.name, 3034 | is_static_member(func, base_obj_type), 3035 | ) 3036 | func_metadata[func.name]["return_type"] = func_metadata[original_func.name][ 3037 | "return_type" 3038 | ] 3039 | func_metadata[func.name]["args"] = func_metadata[original_func.name]["args"] 3040 | generated_funcs[func.name] = True # completed generating the function 3041 | return 3042 | func_prototypes[prototype_str] = func 3043 | 3044 | # user_data argument must be handled first, if it exists 3045 | try: 3046 | i = [(arg.name if hasattr(arg, "name") else None) for arg in args].index( 3047 | "user_data" 3048 | ) 3049 | if i > 0: 3050 | enumerated_args = [ 3051 | (i, arg) for i, arg in enumerated_args 3052 | ] # convert enumerate to list 3053 | enumerated_args[0], enumerated_args[i] = ( 3054 | enumerated_args[i], 3055 | enumerated_args[0], 3056 | ) 3057 | except ValueError: 3058 | pass 3059 | 3060 | return_type = get_type(func.type.type, remove_quals=False) 3061 | qualified_return_type = gen.visit(func.type.type) 3062 | if isinstance(func.type.type, c_ast.PtrDecl) and lv_func_returns_array.match( 3063 | func.name 3064 | ): 3065 | try_generate_array_type(func.type.type) 3066 | # print('/* --> return_type = %s, qualified_return_type = %s\n%s */' % (return_type, qualified_return_type, func.type.type)) 3067 | if return_type == "void": 3068 | build_result = "" 3069 | build_return_value = "mp_const_none" 3070 | func_metadata[func.name]["return_type"] = "NoneType" 3071 | else: 3072 | if return_type not in lv_to_mp or not lv_to_mp[return_type]: 3073 | try_generate_type(func.type.type) 3074 | if return_type not in lv_to_mp or not lv_to_mp[return_type]: 3075 | raise MissingConversionException( 3076 | "Missing conversion from %s" % return_type 3077 | ) 3078 | build_result = "%s _res = " % qualified_return_type 3079 | cast = ( 3080 | "(void*)" if isinstance(func.type.type, c_ast.PtrDecl) else "" 3081 | ) # needed when field is const. casting to void overrides it 3082 | build_return_value = "{type}({cast}_res)".format( 3083 | type=lv_to_mp[return_type], cast=cast 3084 | ) 3085 | func_metadata[func.name]["return_type"] = lv_mp_type[return_type] 3086 | print( 3087 | """ 3088 | /* 3089 | * {module_name} extension definition for: 3090 | * {print_func} 3091 | */ 3092 | 3093 | static mp_obj_t mp_{func}(size_t mp_n_args, const mp_obj_t *mp_args, void *lv_func_ptr) 3094 | {{ 3095 | {build_args} 3096 | {build_result}(({func_ptr})lv_func_ptr)({send_args}); 3097 | return {build_return_value}; 3098 | }} 3099 | 3100 | """.format( 3101 | module_name=module_name, 3102 | func=func.name, 3103 | func_ptr=prototype_str, 3104 | print_func=gen.visit(func), 3105 | build_args="\n ".join( 3106 | [ 3107 | build_mp_func_arg(arg, i, func, obj_name) 3108 | for i, arg in enumerated_args 3109 | if isinstance(arg, c_ast.EllipsisParam) 3110 | or (not isinstance(arg.type, c_ast.TypeDecl)) 3111 | or (not isinstance(arg.type.type, c_ast.IdentifierType)) 3112 | or "void" not in arg.type.type.names 3113 | ] 3114 | ), # Handle the case of 'void' param which should be ignored 3115 | send_args=", ".join( 3116 | [ 3117 | (arg.name if (hasattr(arg, "name") and arg.name) else ("arg%d" % i)) 3118 | for i, arg in enumerate(args) 3119 | ] 3120 | ), 3121 | build_result=build_result, 3122 | build_return_value=build_return_value, 3123 | ) 3124 | ) 3125 | 3126 | emit_func_obj( 3127 | func.name, 3128 | func.name, 3129 | param_count, 3130 | func.name, 3131 | is_static_member(func, base_obj_type), 3132 | ) 3133 | generated_funcs[func.name] = True # completed generating the function 3134 | # print('/* is_struct_function() = %s, is_static_member() = %s, get_first_arg_type()=%s, obj_name = %s */' % ( 3135 | # is_struct_function(func), is_static_member(func, base_obj_type), get_first_arg_type(func), base_obj_type)) 3136 | 3137 | 3138 | def gen_func_error(method, exp): 3139 | global funcs 3140 | print( 3141 | """ 3142 | /* 3143 | * Function NOT generated: 3144 | * {problem} 3145 | * {method} 3146 | */ 3147 | """.format( 3148 | method=gen.visit(method) if isinstance(method, c_ast.Node) else method, 3149 | problem=exp, 3150 | ) 3151 | ) 3152 | try: 3153 | funcs.remove(method) 3154 | except: 3155 | pass 3156 | 3157 | 3158 | # 3159 | # Emit Mpy objects definitions 3160 | # 3161 | 3162 | enum_referenced = collections.OrderedDict() 3163 | 3164 | 3165 | def gen_obj_methods(obj_name): 3166 | global enums 3167 | helper_members = ( 3168 | ["{ MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&cast_obj_class_method) }"] 3169 | if len(obj_names) > 0 and obj_name == base_obj_name 3170 | else [] 3171 | ) 3172 | members = [ 3173 | "{{ MP_ROM_QSTR(MP_QSTR_{method_name}), MP_ROM_PTR(&mp_{method}_mpobj) }}".format( 3174 | method=method.name, 3175 | method_name=sanitize(method_name_from_func_name(method.name)), 3176 | ) 3177 | for method in get_methods(obj_name) 3178 | ] 3179 | obj_metadata[obj_name]["members"].update( 3180 | { 3181 | method_name_from_func_name(method.name): func_metadata[method.name] 3182 | for method in get_methods(obj_name) 3183 | } 3184 | ) 3185 | # add parent methods 3186 | parent_members = [] 3187 | if obj_name in parent_obj_names and parent_obj_names[obj_name] != None: 3188 | # parent_members += gen_obj_methods(parent_obj_names[obj_name]) 3189 | obj_metadata[obj_name]["members"].update( 3190 | obj_metadata[parent_obj_names[obj_name]]["members"] 3191 | ) 3192 | # add enum members 3193 | enum_members = [ 3194 | "{{ MP_ROM_QSTR(MP_QSTR_{enum_member}), MP_ROM_PTR({enum_member_value}) }}".format( 3195 | enum_member=sanitize(get_enum_member_name(enum_member_name)), 3196 | enum_member_value=get_enum_value(obj_name, enum_member_name), 3197 | ) 3198 | for enum_member_name in get_enum_members(obj_name) 3199 | ] 3200 | obj_metadata[obj_name]["members"].update( 3201 | { 3202 | get_enum_member_name(enum_member_name): {"type": "enum_member"} 3203 | for enum_member_name in get_enum_members(obj_name) 3204 | } 3205 | ) 3206 | # add enums that match object name 3207 | obj_enums = [ 3208 | enum_name for enum_name in enums.keys() if is_method_of(enum_name, obj_name) 3209 | ] 3210 | enum_types = [ 3211 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }}".format( 3212 | name=sanitize(method_name_from_func_name(enum_name)), enum=enum_name 3213 | ) 3214 | for enum_name in obj_enums 3215 | ] 3216 | obj_metadata[obj_name]["members"].update( 3217 | { 3218 | method_name_from_func_name(enum_name): {"type": "enum_type"} 3219 | for enum_name in obj_enums 3220 | } 3221 | ) 3222 | for enum_name in obj_enums: 3223 | obj_metadata[obj_name]["members"][method_name_from_func_name(enum_name)].update( 3224 | obj_metadata[enum_name] 3225 | ) 3226 | enum_referenced[enum_name] = True 3227 | return members + parent_members + enum_members + enum_types + helper_members 3228 | 3229 | 3230 | def gen_obj(obj_name): 3231 | # eprint('Generating object %s...' % obj_name) 3232 | is_obj = has_ctor(obj_name) 3233 | should_add_base_methods = is_obj and obj_name != "obj" 3234 | obj_metadata[obj_name] = {"members": collections.OrderedDict()} 3235 | 3236 | # Generate object methods 3237 | for method in get_methods(obj_name): 3238 | try: 3239 | gen_mp_func(method, obj_name) 3240 | except MissingConversionException as exp: 3241 | gen_func_error(method, exp) 3242 | 3243 | # Generate object construction function, if needed 3244 | if is_obj: 3245 | ctor_func = get_ctor(obj_name) 3246 | try: 3247 | gen_mp_func(ctor_func, obj_name) 3248 | except MissingConversionException as exp: 3249 | gen_func_error(ctor_func, exp) 3250 | 3251 | # print([method.name for method in methods]) 3252 | ctor = """ 3253 | static mp_obj_t {obj}_make_new( 3254 | const mp_obj_type_t *type, 3255 | size_t n_args, 3256 | size_t n_kw, 3257 | const mp_obj_t *args) 3258 | {{ 3259 | return make_new(&mp_{ctor_name}_mpobj, type, n_args, n_kw, args); 3260 | }} 3261 | """ 3262 | 3263 | print( 3264 | """ 3265 | /* 3266 | * {module_name} {obj} object definitions 3267 | */ 3268 | """.format(module_name=module_name, obj=obj_name) 3269 | ) 3270 | 3271 | print( 3272 | """ 3273 | static const mp_rom_map_elem_t {obj}_locals_dict_table[] = {{ 3274 | {locals_dict_entries} 3275 | }}; 3276 | 3277 | static MP_DEFINE_CONST_DICT({obj}_locals_dict, {obj}_locals_dict_table); 3278 | 3279 | static void {obj}_print(const mp_print_t *print, 3280 | mp_obj_t self_in, 3281 | mp_print_kind_t kind) 3282 | {{ 3283 | mp_printf(print, "{module_name} {obj}"); 3284 | }} 3285 | 3286 | {ctor} 3287 | 3288 | static MP_DEFINE_CONST_OBJ_TYPE( 3289 | mp_lv_{obj}_type_base, 3290 | MP_QSTR_{obj}, 3291 | MP_TYPE_FLAG_NONE, 3292 | print, {obj}_print, 3293 | {make_new} 3294 | {binary_op} 3295 | attr, call_parent_methods, 3296 | {buffer} 3297 | {parent} 3298 | locals_dict, &{obj}_locals_dict 3299 | ); 3300 | 3301 | GENMPY_UNUSED static const mp_lv_obj_type_t mp_lv_{obj}_type = {{ 3302 | #ifdef LV_OBJ_T 3303 | .lv_obj_class = {lv_class}, 3304 | #endif 3305 | .mp_obj_type = &mp_lv_{obj}_type_base, 3306 | }}; 3307 | """.format( 3308 | module_name=module_name, 3309 | obj=sanitize(obj_name), 3310 | locals_dict_entries=",\n ".join(gen_obj_methods(obj_name)), 3311 | ctor=ctor.format(obj=obj_name, ctor_name=ctor_func.name) 3312 | if has_ctor(obj_name) 3313 | else "", 3314 | make_new="make_new, %s_make_new," % obj_name if is_obj else "", 3315 | binary_op="binary_op, mp_lv_obj_binary_op," if is_obj else "", 3316 | buffer="buffer, mp_lv_obj_get_buffer," if is_obj else "", 3317 | parent="parent, &mp_lv_%s_type_base," % parent_obj_names[obj_name] 3318 | if obj_name in parent_obj_names and parent_obj_names[obj_name] 3319 | else "", 3320 | lv_class="&lv_%s_class" % obj_name if is_obj else "NULL", 3321 | ) 3322 | ) 3323 | 3324 | 3325 | # 3326 | # Generate Enum objects 3327 | # 3328 | 3329 | for enum_name in list(enums.keys()): 3330 | gen_obj(enum_name) 3331 | 3332 | # 3333 | # Generate all other objects. Generate parent objects first 3334 | # 3335 | 3336 | generated_obj_names = collections.OrderedDict() 3337 | for obj_name in obj_names: 3338 | # eprint("--> %s [%s]" % (obj_name, ", ".join([name for name in generated_obj_names]))) 3339 | parent_obj_name = ( 3340 | parent_obj_names[obj_name] if obj_name in parent_obj_names else None 3341 | ) 3342 | 3343 | while parent_obj_name != None and parent_obj_name not in generated_obj_names: 3344 | gen_obj(parent_obj_name) 3345 | generated_obj_names[parent_obj_name] = True 3346 | parent_obj_name = ( 3347 | parent_obj_names[parent_obj_name] 3348 | if parent_obj_name in parent_obj_names 3349 | else None 3350 | ) 3351 | 3352 | if obj_name not in generated_obj_names: 3353 | # eprint("--> gen obj %s" % obj_name) 3354 | gen_obj(obj_name) 3355 | generated_obj_names[obj_name] = True 3356 | 3357 | # 3358 | # Generate structs which contain function members 3359 | # First argument of a function could be it's parent struct 3360 | # Need to make sure these structs are generated *before* struct-functions are 3361 | # Otherwise we will not know of all the structs when generating struct-functions 3362 | # 3363 | 3364 | 3365 | def try_generate_structs_from_first_argument(): 3366 | for func in funcs: 3367 | if func.name in generated_funcs: 3368 | continue 3369 | args = func.type.args.params if func.type.args else [] 3370 | if len(args) < 1: 3371 | continue 3372 | arg_type = get_type(args[0].type, remove_quals=True) 3373 | if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: 3374 | try: 3375 | try_generate_type(args[0].type) 3376 | except MissingConversionException as e: 3377 | print( 3378 | """ 3379 | /* 3380 | * {struct} not generated: {err} 3381 | */ 3382 | """.format(struct=arg_type, err=e) 3383 | ) 3384 | 3385 | 3386 | # 3387 | # Generate globals 3388 | # 3389 | 3390 | # eprint("/* Generating globals */") 3391 | 3392 | 3393 | def gen_global(global_name, global_type_ast): 3394 | global_type = get_type(global_type_ast, remove_quals=True) 3395 | generated_global = try_generate_type(global_type_ast) 3396 | # print("/* generated_global = %s */" % generated_global) 3397 | if global_type not in generated_structs: 3398 | wrapped_type = lv_mp_type[global_type] 3399 | if not wrapped_type: 3400 | raise MissingConversionException( 3401 | "Missing conversion to %s when generating global %s" 3402 | % (wrapped_type, global_name) 3403 | ) 3404 | global_type = sanitize("_lv_mp_%s_wrapper" % wrapped_type) 3405 | custom_struct_str = """ 3406 | typedef struct {{ 3407 | {type} value; 3408 | }} {name}; 3409 | """.format(type=wrapped_type, name=global_type) 3410 | if global_type not in generated_structs: 3411 | print("/* Global struct wrapper for %s */" % wrapped_type) 3412 | print(custom_struct_str) 3413 | # eprint("%s: %s\n" % (wrapped_type, custom_struct_str)) 3414 | try_generate_struct( 3415 | global_type, parser.parse(custom_struct_str).ext[0].type.type 3416 | ) 3417 | 3418 | print( 3419 | """ 3420 | /* 3421 | * {module_name} {global_name} global definitions 3422 | */ 3423 | 3424 | static const mp_lv_struct_t mp_{global_name} = {{ 3425 | {{ &mp_{struct_name}_type }}, 3426 | ({cast}*)&{global_name} 3427 | }}; 3428 | """.format( 3429 | module_name=module_name, 3430 | global_name=global_name, 3431 | struct_name=global_type, 3432 | cast=gen.visit(global_type_ast), 3433 | ) 3434 | ) 3435 | 3436 | 3437 | generated_globals = [] 3438 | for global_name in blobs: 3439 | try: 3440 | gen_global(global_name, blobs[global_name]) 3441 | generated_globals.append(global_name) 3442 | except MissingConversionException as exp: 3443 | gen_func_error(global_name, exp) 3444 | 3445 | # 3446 | # Generate struct-functions 3447 | # 3448 | 3449 | # eprint("/* Generating struct-functions */") 3450 | try_generate_structs_from_first_argument() 3451 | 3452 | 3453 | def generate_struct_functions(struct_list): 3454 | # print('/* List of structs: %s */' % repr(struct_list)) 3455 | for struct_name in struct_list: 3456 | if not generated_structs[struct_name]: 3457 | continue 3458 | sanitized_struct_name = sanitize(struct_name) 3459 | struct_funcs = get_struct_functions(struct_name) 3460 | # print('/* Struct %s contains: %s */' % (struct_name, [f.name for f in struct_funcs])) 3461 | for struct_func in struct_funcs[ 3462 | : 3463 | ]: # clone list because we are changing it in the loop. 3464 | try: 3465 | if struct_func.name not in generated_funcs: 3466 | gen_mp_func(struct_func, struct_name) 3467 | except MissingConversionException as exp: 3468 | gen_func_error(struct_func, exp) 3469 | struct_funcs.remove(struct_func) 3470 | if struct_name not in structs or structs[struct_name].decls: 3471 | struct_size_attr = "{{ MP_ROM_QSTR(MP_QSTR___SIZE__), MP_ROM_PTR(MP_ROM_INT(sizeof({struct_tag}{struct_name}))) }},".format( 3472 | struct_name=struct_name, 3473 | struct_tag="struct " 3474 | if struct_name in structs_without_typedef.keys() 3475 | else "", 3476 | ) 3477 | else: 3478 | struct_size_attr = "" 3479 | print( 3480 | """ 3481 | static const mp_rom_map_elem_t mp_{sanitized_struct_name}_locals_dict_table[] = {{ 3482 | {struct_size} 3483 | {functions} 3484 | }}; 3485 | 3486 | static MP_DEFINE_CONST_DICT(mp_{sanitized_struct_name}_locals_dict, mp_{sanitized_struct_name}_locals_dict_table); 3487 | """.format( 3488 | struct_size=struct_size_attr, 3489 | sanitized_struct_name=sanitized_struct_name, 3490 | functions="".join( 3491 | [ 3492 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n ".format( 3493 | name=sanitize(noncommon_part(f.name, struct_name)), 3494 | func=f.name, 3495 | ) 3496 | for f in struct_funcs 3497 | ] 3498 | ), 3499 | ) 3500 | ) 3501 | 3502 | generated_struct_functions[struct_name] = True 3503 | 3504 | 3505 | generate_struct_functions(list(generated_structs.keys())) 3506 | 3507 | # 3508 | # Generate all module functions (not including method functions which were already generated) 3509 | # 3510 | 3511 | print( 3512 | """ 3513 | /* 3514 | * 3515 | * Global Module Functions 3516 | * 3517 | */ 3518 | """ 3519 | ) 3520 | 3521 | # eprint("/* Generating global module functions /*") 3522 | module_funcs = [func for func in funcs if func.name not in generated_funcs] 3523 | for module_func in module_funcs[ 3524 | : 3525 | ]: # clone list because we are changing it in the loop. 3526 | if module_func.name in generated_funcs: 3527 | continue # generated_funcs could change inside the loop so need to recheck. 3528 | try: 3529 | gen_mp_func(module_func, None) 3530 | # A new function can create new struct with new function structs 3531 | new_structs = [ 3532 | s for s in generated_structs if s not in generated_struct_functions 3533 | ] 3534 | if new_structs: 3535 | generate_struct_functions(new_structs) 3536 | except MissingConversionException as exp: 3537 | gen_func_error(module_func, exp) 3538 | module_funcs.remove(module_func) 3539 | 3540 | functions_not_generated = [ 3541 | func.name for func in funcs if func.name not in generated_funcs 3542 | ] 3543 | if len(functions_not_generated) > 0: 3544 | print( 3545 | """ 3546 | /* 3547 | * Functions not generated: 3548 | * {funcs} 3549 | * 3550 | */ 3551 | 3552 | """.format(funcs="\n * ".join(functions_not_generated)) 3553 | ) 3554 | 3555 | # 3556 | # Generate callback functions 3557 | # 3558 | 3559 | # for func_typedef in func_typedefs: 3560 | # func = func_typedef.type.type 3561 | # try: 3562 | # gen_callback_func(func) 3563 | # except MissingConversionException as exp: 3564 | # gen_func_error(func, exp) 3565 | # func_name = get_arg_name(func.type) 3566 | # lv_to_mp[func_name] = lv_to_mp['void *'] 3567 | # mp_to_lv[func_name] = mp_to_lv['void *'] 3568 | 3569 | # eprint("/* Generating callback functions */") 3570 | for func_name, func, struct_name in callbacks_used_on_structs: 3571 | try: 3572 | # print('/* --> gen_callback_func %s */' % func_name) 3573 | gen_callback_func(func, func_name="%s_%s" % (struct_name, func_name)) 3574 | except MissingConversionException as exp: 3575 | gen_func_error(func, exp) 3576 | # func_name = get_arg_name(func.type) 3577 | # lv_to_mp[func_name] = lv_to_mp['void *'] 3578 | # mp_to_lv[func_name] = mp_to_lv['void *'] 3579 | 3580 | # 3581 | # Emit Mpy Module definition 3582 | # 3583 | 3584 | # eprint("/* Generating module definition */") 3585 | print( 3586 | """ 3587 | 3588 | /* 3589 | * {module_name} module definitions 3590 | */ 3591 | 3592 | static const mp_rom_map_elem_t {module_name}_globals_table[] = {{ 3593 | {{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_{module_name}) }}, 3594 | {objects} 3595 | {functions} 3596 | {enums} 3597 | {structs} 3598 | {struct_aliases} 3599 | {blobs} 3600 | {int_constants} 3601 | #ifdef LV_OBJ_T 3602 | {{ MP_ROM_QSTR(MP_QSTR_LvReferenceError), MP_ROM_PTR(&mp_type_LvReferenceError) }}, 3603 | #endif // LV_OBJ_T 3604 | }}; 3605 | """.format( 3606 | module_name=sanitize(module_name), 3607 | objects="".join( 3608 | [ 3609 | "{{ MP_ROM_QSTR(MP_QSTR_{obj}), MP_ROM_PTR(&mp_lv_{obj}_type_base) }},\n ".format( 3610 | obj=sanitize(o) 3611 | ) 3612 | for o in obj_names 3613 | ] 3614 | ), 3615 | functions="".join( 3616 | [ 3617 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n ".format( 3618 | name=sanitize(simplify_identifier(f.name)), func=f.name 3619 | ) 3620 | for f in module_funcs 3621 | ] 3622 | ), 3623 | enums="".join( 3624 | [ 3625 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }},\n ".format( 3626 | name=sanitize(get_enum_name(enum_name)), enum=enum_name 3627 | ) 3628 | for enum_name in enums.keys() 3629 | if enum_name not in enum_referenced 3630 | ] 3631 | ), 3632 | structs="".join( 3633 | [ 3634 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n ".format( 3635 | name=sanitize(simplify_identifier(struct_name)), 3636 | struct_name=sanitize(struct_name), 3637 | ) 3638 | for struct_name in generated_structs 3639 | if generated_structs[struct_name] 3640 | ] 3641 | ), 3642 | struct_aliases="".join( 3643 | [ 3644 | "{{ MP_ROM_QSTR(MP_QSTR_{alias_name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n ".format( 3645 | struct_name=sanitize(struct_name), 3646 | alias_name=sanitize( 3647 | simplify_identifier(struct_aliases[struct_name]) 3648 | ), 3649 | ) 3650 | for struct_name in struct_aliases.keys() 3651 | ] 3652 | ), 3653 | blobs="".join( 3654 | [ 3655 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},\n ".format( 3656 | name=sanitize(simplify_identifier(global_name)), 3657 | global_name=global_name, 3658 | ) 3659 | for global_name in generated_globals 3660 | ] 3661 | ), 3662 | int_constants="".join( 3663 | [ 3664 | "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(MP_ROM_INT({value})) }},\n ".format( 3665 | name=sanitize(get_enum_name(int_constant)), value=int_constant 3666 | ) 3667 | for int_constant in int_constants 3668 | ] 3669 | ), 3670 | ) 3671 | ) 3672 | 3673 | 3674 | print( 3675 | """ 3676 | static MP_DEFINE_CONST_DICT ( 3677 | mp_module_{module_name}_globals, 3678 | {module_name}_globals_table 3679 | ); 3680 | 3681 | const mp_obj_module_t mp_module_{module_name} = {{ 3682 | .base = {{ &mp_type_module }}, 3683 | .globals = (mp_obj_dict_t*)&mp_module_{module_name}_globals 3684 | }}; 3685 | 3686 | MP_REGISTER_MODULE(MP_QSTR_{module_name}, mp_module_{module_name}); 3687 | 3688 | """.format( 3689 | module_name=module_name, 3690 | ) 3691 | ) 3692 | 3693 | # Add an array of all object types 3694 | 3695 | if len(obj_names) > 0: 3696 | print( 3697 | """ 3698 | static const mp_lv_obj_type_t *mp_lv_obj_types[] = {{ 3699 | {obj_types}, 3700 | NULL 3701 | }}; 3702 | """.format( 3703 | obj_types=",\n ".join( 3704 | ["&mp_lv_%s_type" % obj_name for obj_name in obj_names] 3705 | ) 3706 | ) 3707 | ) 3708 | 3709 | # Save Metadata File, if specified. 3710 | 3711 | if args.metadata: 3712 | metadata = collections.OrderedDict() 3713 | metadata["objects"] = {obj_name: obj_metadata[obj_name] for obj_name in obj_names} 3714 | metadata["functions"] = { 3715 | simplify_identifier(f.name): func_metadata[f.name] for f in module_funcs 3716 | } 3717 | metadata["enums"] = { 3718 | get_enum_name(enum_name): obj_metadata[enum_name] 3719 | for enum_name in enums.keys() 3720 | if enum_name not in enum_referenced 3721 | } 3722 | metadata["structs"] = [ 3723 | simplify_identifier(struct_name) 3724 | for struct_name in generated_structs 3725 | if struct_name in generated_structs 3726 | ] 3727 | metadata["structs"] += [ 3728 | simplify_identifier(struct_aliases[struct_name]) 3729 | for struct_name in struct_aliases.keys() 3730 | ] 3731 | metadata["blobs"] = [ 3732 | simplify_identifier(global_name) for global_name in generated_globals 3733 | ] 3734 | metadata["int_constants"] = [ 3735 | get_enum_name(int_constant) for int_constant in int_constants 3736 | ] 3737 | 3738 | # TODO: struct functions 3739 | 3740 | with open(args.metadata, "w") as metadata_file: 3741 | json.dump(metadata, metadata_file, indent=4) 3742 | --------------------------------------------------------------------------------