From 4f973a922e7dbf67ee6dc13954afa42877435b37 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:49:36 -0500 Subject: [PATCH 1/2] Fixed-size struct arrays in structs read as views and check assignments draw_triangle_dsc_t.p, grad_dsc_t.stops and the other struct-array fields now index, write through and take len() on every interpreter. MicroPython returned a list of copies, so writes were lost; CPython returned a Blob, and assigning a list memcpy'd from NULL. Whole-array assignment takes exactly N structs or dicts, or a buffer of the array's size, and otherwise raises and leaves the field unchanged. Fixes #38. --- binding/emit_c_cpython.py | 136 +++++++++++++++++++++++++ binding/emit_c_micropython_style.py | 150 ++++++++++++++++++++++++++- binding/emit_cpython_native.py | 47 ++++++++- generated/lvgl_circuitpython.c | 125 +++++++++++++++++++++-- generated/lvgl_micropython.c | 125 +++++++++++++++++++++-- generated/lvgl_python.c | 152 ++++++++++++++++++++++++++-- tools/test_lvgl_smoke.py | 51 ++++++++++ 7 files changed, 756 insertions(+), 30 deletions(-) diff --git a/binding/emit_c_cpython.py b/binding/emit_c_cpython.py index dbda23d..1aed528 100644 --- a/binding/emit_c_cpython.py +++ b/binding/emit_c_cpython.py @@ -289,6 +289,142 @@ def emit_c(ctx): if (rc != 0) return NULL; return arr; } + +/* + * Fixed-size struct arrays inside a struct (lv_point_precise_t p[3]). + * + * Reading the field gives a view over the parent struct's memory, so + * d.p[1].x = 7 and d.p[1] = point write through, and len(d.p) works. The view + * holds the parent so the memory stays alive. Assigning the field takes a + * sequence of exactly that many structs (or dicts), or a packed buffer of the + * array's size; anything else raises and leaves the field unchanged + * (lvgl-bindings#38). + */ + +typedef PyObject *(*lvpy_struct_reader_t)(void *); + +typedef struct { + PyObject_HEAD + PyObject *owner; + char *data; + Py_ssize_t len; + size_t elem_size; + lvpy_struct_reader_t read; + lvpy_struct_writer_t write; +} lvpy_struct_array_t; + +static void lvpy_struct_array_dealloc(PyObject *self) +{ + Py_XDECREF(((lvpy_struct_array_t *)self)->owner); + Py_TYPE(self)->tp_free(self); +} + +static Py_ssize_t lvpy_struct_array_len(PyObject *self) +{ + return ((lvpy_struct_array_t *)self)->len; +} + +static PyObject *lvpy_struct_array_item(PyObject *self_in, Py_ssize_t i) +{ + lvpy_struct_array_t *self = (lvpy_struct_array_t *)self_in; + if (i < 0 || i >= self->len) { + PyErr_SetString(PyExc_IndexError, "struct array index out of range"); + return NULL; + } + return self->read(self->data + (size_t)i * self->elem_size); +} + +static int lvpy_struct_array_ass_item(PyObject *self_in, Py_ssize_t i, PyObject *value) +{ + lvpy_struct_array_t *self = (lvpy_struct_array_t *)self_in; + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "can't delete struct array items"); + return -1; + } + if (i < 0 || i >= self->len) { + PyErr_SetString(PyExc_IndexError, "struct array assignment index out of range"); + return -1; + } + void *p = self->write(value); + if (PyErr_Occurred()) return -1; + memcpy(self->data + (size_t)i * self->elem_size, p, self->elem_size); + return 0; +} + +static PySequenceMethods lvpy_struct_array_as_sequence = { + .sq_length = lvpy_struct_array_len, + .sq_item = lvpy_struct_array_item, + .sq_ass_item = lvpy_struct_array_ass_item, +}; + +static PyTypeObject lvpy_struct_array_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "lvgl.StructArray", + .tp_basicsize = sizeof(lvpy_struct_array_t), + .tp_dealloc = lvpy_struct_array_dealloc, + .tp_as_sequence = &lvpy_struct_array_as_sequence, + .tp_flags = Py_TPFLAGS_DEFAULT, +}; + +static inline PyObject *lvpy_struct_array_new(PyObject *owner, void *data, + Py_ssize_t len, size_t elem_size, lvpy_struct_reader_t read, lvpy_struct_writer_t write) +{ + if (!(lvpy_struct_array_type.tp_flags & Py_TPFLAGS_READY) && + PyType_Ready(&lvpy_struct_array_type) < 0) + return NULL; + lvpy_struct_array_t *self = PyObject_New(lvpy_struct_array_t, &lvpy_struct_array_type); + if (!self) return NULL; + Py_XINCREF(owner); + self->owner = owner; + self->data = (char *)data; + self->len = len; + self->elem_size = elem_size; + self->read = read; + self->write = write; + return (PyObject *)self; +} + +/* Sets an exception and leaves data unchanged on any mismatch. */ +static inline void lvpy_struct_array_assign(void *data, Py_ssize_t len, + size_t elem_size, lvpy_struct_writer_t write, PyObject *value) +{ + size_t total = (size_t)len * elem_size; + if (PyBytes_Check(value) || PyByteArray_Check(value) || PyMemoryView_Check(value)) { + Py_buffer view; + if (PyObject_GetBuffer(value, &view, PyBUF_C_CONTIGUOUS) < 0) return; + if ((size_t)view.len != total) + PyErr_Format(PyExc_ValueError, "expected a buffer of %zu bytes, got %zd", + total, view.len); + else + memcpy(data, view.buf, total); + PyBuffer_Release(&view); + return; + } + if (!PySequence_Check(value) || PyUnicode_Check(value)) { + PyErr_Format(PyExc_TypeError, "expected a sequence of %zd structs, got '%s'", + len, Py_TYPE(value)->tp_name); + return; + } + PyObject *items = PySequence_Tuple(value); + if (!items) return; + if (PyTuple_GET_SIZE(items) != len) { + PyErr_Format(PyExc_ValueError, "expected %zd items, got %zd", + len, PyTuple_GET_SIZE(items)); + Py_DECREF(items); + return; + } + /* Convert every item before writing any, so a bad item changes nothing. */ + char *tmp = (char *)PyMem_Malloc(total ? total : 1); + if (!tmp) { Py_DECREF(items); PyErr_NoMemory(); return; } + for (Py_ssize_t i = 0; i < len; i++) { + void *p = write(PyTuple_GET_ITEM(items, i)); + if (PyErr_Occurred()) { PyMem_Free(tmp); Py_DECREF(items); return; } + memcpy(tmp + (size_t)i * elem_size, p, elem_size); + } + memcpy(data, tmp, total); + PyMem_Free(tmp); + Py_DECREF(items); +} """ ) diff --git a/binding/emit_c_micropython_style.py b/binding/emit_c_micropython_style.py index 20f7d47..394c691 100644 --- a/binding/emit_c_micropython_style.py +++ b/binding/emit_c_micropython_style.py @@ -1131,6 +1131,111 @@ def emit_c(ctx): locals_dict, &mp_base_struct_locals_dict ); +/* + * Fixed-size struct arrays inside a struct (lv_point_precise_t p[3]). + * + * Reading the field gives a view over the parent struct's memory, so + * d.p[1].x = 7 and d.p[1] = point write through, and len(d.p) works. The view + * holds the parent so the memory stays alive. Assigning the field takes a + * sequence of exactly that many structs (or dicts), or a packed buffer of the + * array's size; anything else raises and leaves the field unchanged + * (lvgl-bindings#38). + */ + +typedef struct { + mp_obj_base_t base; + mp_obj_t owner; + char *data; + size_t len; + size_t elem_size; + mp_obj_t (*read)(void *elem); + void *(*write)(mp_obj_t value); +} mp_lv_struct_array_t; + +static mp_obj_t lv_struct_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + if (value == MP_OBJ_NULL) { + mp_raise_TypeError(MP_ERROR_TEXT("can't delete struct array items")); + } + size_t i = mp_get_index(self->base.type, self->len, index, false); + char *elem = self->data + i * self->elem_size; + if (value == MP_OBJ_SENTINEL) { + return self->read(elem); + } + memcpy(elem, self->write(value), self->elem_size); + return mp_const_none; +} + +static mp_obj_t lv_struct_array_unary_op(mp_unary_op_t op, mp_obj_t self_in) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0); + default: return MP_OBJ_NULL; + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_lv_struct_array_type, + MP_QSTR_StructArray, + MP_TYPE_FLAG_NONE, + unary_op, lv_struct_array_unary_op, + subscr, lv_struct_array_subscr +); + +GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data, + size_t len, size_t elem_size, mp_obj_t (*read)(void *), void *(*write)(mp_obj_t)) +{ + mp_lv_struct_array_t *self = m_new_obj(mp_lv_struct_array_t); + self->base.type = &mp_lv_struct_array_type; + self->owner = owner; + self->data = (char *)data; + self->len = len; + self->elem_size = elem_size; + self->read = read; + self->write = write; + return MP_OBJ_FROM_PTR(self); +} + +GENMPY_UNUSED static void mp_lv_struct_array_assign(void *data, size_t len, + size_t elem_size, void *(*write)(mp_obj_t), mp_obj_t value) +{ + if (MP_OBJ_IS_STR_OR_BYTES(value) || + MP_OBJ_IS_TYPE(value, &mp_type_bytearray) || + MP_OBJ_IS_TYPE(value, &mp_type_memoryview)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != len * elem_size) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected a buffer of %d bytes, got %d"), + (int)(len * elem_size), (int)bufinfo.len); + } + memcpy(data, bufinfo.buf, bufinfo.len); + return; + } + mp_obj_t mp_len = mp_obj_len_maybe(value); + if (mp_len == MP_OBJ_NULL) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("expected a sequence of %d structs, got '%s'"), + (int)len, mp_obj_get_type_str(value)); + } + if ((size_t)mp_obj_get_int(mp_len) != len) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected %d items, got %d"), + (int)len, (int)mp_obj_get_int(mp_len)); + } + // Convert every item before writing any, so a bad item changes nothing. + char *tmp = m_new(char, len * elem_size); + for (size_t i = 0; i < len; i++) { + mp_obj_t item = mp_obj_subscr(value, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL); + memcpy(tmp + i * elem_size, write(item), elem_size); + } + memcpy(data, tmp, len * elem_size); + m_del(char, tmp, len * elem_size); +} + GENMPY_UNUSED static mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) { mp_lv_array_t *self = m_new_obj(mp_lv_array_t); @@ -1440,6 +1545,18 @@ def flatten_struct(struct_decls): return result + def _struct_array_elem_rw(decl): + """(reader, writer) by pointer for a struct-element array field, else None.""" + elem_ast = decl.type.type + if not isinstance(elem_ast, c_ast.TypeDecl): + return None + ptr_type = "%s *" % get_type(elem_ast, remove_quals=True) + read = lv_to_mp.get(ptr_type) or "" + write = mp_to_lv.get(ptr_type) or "" + if read.startswith("mp_read_ptr_") and write.startswith("mp_write_ptr_"): + return read, write + return None + def try_generate_struct(struct_name, struct): if struct_name in generated_structs: return None @@ -1581,7 +1698,38 @@ def try_generate_struct(struct_name, struct): not hasattr(decl.type, "quals") ) or "const" not in decl.type.quals # Arrays must be handled by memcpy, otherwise we would get "assignment to expression with array type" error - if isinstance(decl.type, c_ast.ArrayDecl): + elem_rw = ( + _struct_array_elem_rw(decl) + if isinstance(decl.type, c_ast.ArrayDecl) + else None + ) + if elem_rw: + # A view, not a list of copies (lvgl-bindings#38). + elem_read, elem_write = elem_rw + shape = "data->{decl_name}, {dim}, sizeof({elem})".format( + decl_name=decl.name, + dim=gen.visit(decl.type.dim), + elem=gen.visit(decl.type.type), + ) + if is_writeable: + write_cases.append( + "case MP_QSTR_{field}: mp_lv_struct_array_assign({shape}, {write}, dest[1]); break; // converting to {type_name}".format( + field=sanitize(decl.name), + shape=shape, + write=elem_write, + type_name=type_name, + ) + ) + read_cases.append( + "case MP_QSTR_{field}: dest[0] = mp_lv_struct_array_new(self_in, {shape}, {read}, {write}); break; // converting from {type_name}".format( + field=sanitize(decl.name), + shape=shape, + read=elem_read, + write=elem_write, + type_name=type_name, + ) + ) + elif isinstance(decl.type, c_ast.ArrayDecl): memcpy_size = "sizeof(%s)*%s" % ( gen.visit(decl.type.type), gen.visit(decl.type.dim), diff --git a/binding/emit_cpython_native.py b/binding/emit_cpython_native.py index 7e7cda9..49cd789 100644 --- a/binding/emit_cpython_native.py +++ b/binding/emit_cpython_native.py @@ -362,7 +362,33 @@ def try_generate_struct_cpython(struct_name, struct): ) else: is_writeable = (not hasattr(decl.type, "quals")) or "const" not in decl.type.quals - if isinstance(decl.type, c_ast.ArrayDecl): + elem = ( + _struct_array_elem(decl, get_type_fn, lv_to_mp) + if isinstance(decl.type, c_ast.ArrayDecl) + else None + ) + if elem: + # A view, not a raw Blob, and a checked whole-array write + # instead of a memcpy from whatever mp_to_ptr returned + # (lvgl-bindings#38). + shape = "data->{decl_name}, {dim}, sizeof({ctype})".format( + decl_name=decl.name, + dim=gen.visit(decl.type.dim), + ctype=gen.visit(decl.type.type), + ) + san = sanitize(elem) + if is_writeable: + write_cases.append( + 'if (strcmp(attr, "{field}") == 0) {{ lvpy_struct_array_assign({shape}, mp_write_value_ptr_{san}, value); result = 0; }}'.format( + field=sanitize(decl.name), shape=shape, san=san + ) + ) + read_cases.append( + 'if (strcmp(attr, "{field}") == 0) return lvpy_struct_array_new(self, {shape}, mp_read_ptr_{san}, mp_write_value_ptr_{san});'.format( + field=sanitize(decl.name), shape=shape, san=san + ) + ) + elif isinstance(decl.type, c_ast.ArrayDecl): memcpy_size = "sizeof(%s)*%s" % ( gen.visit(decl.type.type), gen.visit(decl.type.dim), @@ -929,6 +955,25 @@ def _is_char_ptr_array_arg(arg): ) +def _struct_array_elem(decl, get_type_fn, lv_to_mp): + """Element struct name for a fixed-size struct array field, else None. + + Only complete structs qualify: their mp_write_value_ptr_* writer and + mp_read_ptr_* reader exist. + """ + elem_ast = decl.type.type + if not isinstance(elem_ast, c_ast.TypeDecl): + return None + elem = get_type_fn(elem_ast, remove_quals=True) + if not runtime.get("cpython_struct_sizes", {}).get(elem): + return None + if elem in (_h("structs_without_typedef") or {}): + return None + if not (lv_to_mp.get("%s *" % elem) or "").startswith("mp_read_ptr_"): + return None + return elem + + def _const_struct_array_elem(arg, get_type_fn): """Element struct name for a ``const T name[]`` parameter, else None. diff --git a/generated/lvgl_circuitpython.c b/generated/lvgl_circuitpython.c index d18bb46..0581f78 100644 --- a/generated/lvgl_circuitpython.c +++ b/generated/lvgl_circuitpython.c @@ -983,6 +983,111 @@ static MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &mp_base_struct_locals_dict ); +/* + * Fixed-size struct arrays inside a struct (lv_point_precise_t p[3]). + * + * Reading the field gives a view over the parent struct's memory, so + * d.p[1].x = 7 and d.p[1] = point write through, and len(d.p) works. The view + * holds the parent so the memory stays alive. Assigning the field takes a + * sequence of exactly that many structs (or dicts), or a packed buffer of the + * array's size; anything else raises and leaves the field unchanged + * (lvgl-bindings#38). + */ + +typedef struct { + mp_obj_base_t base; + mp_obj_t owner; + char *data; + size_t len; + size_t elem_size; + mp_obj_t (*read)(void *elem); + void *(*write)(mp_obj_t value); +} mp_lv_struct_array_t; + +static mp_obj_t lv_struct_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + if (value == MP_OBJ_NULL) { + mp_raise_TypeError(MP_ERROR_TEXT("can't delete struct array items")); + } + size_t i = mp_get_index(self->base.type, self->len, index, false); + char *elem = self->data + i * self->elem_size; + if (value == MP_OBJ_SENTINEL) { + return self->read(elem); + } + memcpy(elem, self->write(value), self->elem_size); + return mp_const_none; +} + +static mp_obj_t lv_struct_array_unary_op(mp_unary_op_t op, mp_obj_t self_in) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0); + default: return MP_OBJ_NULL; + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_lv_struct_array_type, + MP_QSTR_StructArray, + MP_TYPE_FLAG_NONE, + unary_op, lv_struct_array_unary_op, + subscr, lv_struct_array_subscr +); + +GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data, + size_t len, size_t elem_size, mp_obj_t (*read)(void *), void *(*write)(mp_obj_t)) +{ + mp_lv_struct_array_t *self = m_new_obj(mp_lv_struct_array_t); + self->base.type = &mp_lv_struct_array_type; + self->owner = owner; + self->data = (char *)data; + self->len = len; + self->elem_size = elem_size; + self->read = read; + self->write = write; + return MP_OBJ_FROM_PTR(self); +} + +GENMPY_UNUSED static void mp_lv_struct_array_assign(void *data, size_t len, + size_t elem_size, void *(*write)(mp_obj_t), mp_obj_t value) +{ + if (MP_OBJ_IS_STR_OR_BYTES(value) || + MP_OBJ_IS_TYPE(value, &mp_type_bytearray) || + MP_OBJ_IS_TYPE(value, &mp_type_memoryview)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != len * elem_size) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected a buffer of %d bytes, got %d"), + (int)(len * elem_size), (int)bufinfo.len); + } + memcpy(data, bufinfo.buf, bufinfo.len); + return; + } + mp_obj_t mp_len = mp_obj_len_maybe(value); + if (mp_len == MP_OBJ_NULL) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("expected a sequence of %d structs, got '%s'"), + (int)len, mp_obj_get_type_str(value)); + } + if ((size_t)mp_obj_get_int(mp_len) != len) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected %d items, got %d"), + (int)len, (int)mp_obj_get_int(mp_len)); + } + // Convert every item before writing any, so a bad item changes nothing. + char *tmp = m_new(char, len * elem_size); + for (size_t i = 0; i < len; i++) { + mp_obj_t item = mp_obj_subscr(value, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL); + memcpy(tmp + i * elem_size, write(item), elem_size); + } + memcpy(data, tmp, len * elem_size); + m_del(char, tmp, len * elem_size); +} + GENMPY_UNUSED static mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) { mp_lv_array_t *self = m_new_obj(mp_lv_array_t); @@ -6243,7 +6348,7 @@ static void mp_lv_grad_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) // load attribute switch(attr) { - case MP_QSTR_stops: dest[0] = mp_arr_from_lv_grad_stop_t___2__(data->stops); break; // converting from lv_grad_stop_t [2]; + case MP_QSTR_stops: dest[0] = mp_lv_struct_array_new(self_in, data->stops, 2, sizeof(lv_grad_stop_t), mp_read_ptr_lv_grad_stop_t, mp_write_ptr_lv_grad_stop_t); break; // converting from lv_grad_stop_t [2]; case MP_QSTR_stops_count: dest[0] = mp_obj_new_int_from_uint(data->stops_count); break; // converting from uint8_t; case MP_QSTR_dir: dest[0] = mp_obj_new_int(data->dir); break; // converting from lv_grad_dir_t; case MP_QSTR_extend: dest[0] = mp_obj_new_int(data->extend); break; // converting from lv_grad_extend_t; @@ -6257,7 +6362,7 @@ static void mp_lv_grad_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) // store attribute switch(attr) { - case MP_QSTR_stops: memcpy((void*)&data->stops, mp_arr_to_lv_grad_stop_t___2__(dest[1]), sizeof(lv_grad_stop_t)*2); break; // converting to lv_grad_stop_t [2]; + case MP_QSTR_stops: mp_lv_struct_array_assign(data->stops, 2, sizeof(lv_grad_stop_t), mp_write_ptr_lv_grad_stop_t, dest[1]); break; // converting to lv_grad_stop_t [2]; case MP_QSTR_stops_count: data->stops_count = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to uint8_t; case MP_QSTR_dir: data->dir = (int)mp_obj_get_int(dest[1]); break; // converting to lv_grad_dir_t; case MP_QSTR_extend: data->extend = (int)mp_obj_get_int(dest[1]); break; // converting to lv_grad_extend_t; @@ -10964,7 +11069,7 @@ static void mp_lv_display_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_stride_is_auto: dest[0] = mp_obj_new_int_from_uint(data->stride_is_auto); break; // converting from uint32_t; case MP_QSTR_rendering_in_progress: dest[0] = mp_obj_new_int_from_uint(data->rendering_in_progress); break; // converting from uint32_t; case MP_QSTR_color_format: dest[0] = mp_obj_new_int(data->color_format); break; // converting from lv_color_format_t; - case MP_QSTR_inv_areas: dest[0] = mp_arr_from_lv_area_t___32__(data->inv_areas); break; // converting from lv_area_t [32]; + case MP_QSTR_inv_areas: dest[0] = mp_lv_struct_array_new(self_in, data->inv_areas, 32, sizeof(lv_area_t), mp_read_ptr_lv_area_t, mp_write_ptr_lv_area_t); break; // converting from lv_area_t [32]; case MP_QSTR_inv_area_joined: dest[0] = mp_arr_from_uint8_t___32__(data->inv_area_joined); break; // converting from uint8_t [32]; case MP_QSTR_inv_p: dest[0] = mp_obj_new_int_from_uint(data->inv_p); break; // converting from uint32_t; case MP_QSTR_inv_en_cnt: dest[0] = mp_obj_new_int(data->inv_en_cnt); break; // converting from int32_t; @@ -11025,7 +11130,7 @@ static void mp_lv_display_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_stride_is_auto: data->stride_is_auto = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_rendering_in_progress: data->rendering_in_progress = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_color_format: data->color_format = (int)mp_obj_get_int(dest[1]); break; // converting to lv_color_format_t; - case MP_QSTR_inv_areas: memcpy((void*)&data->inv_areas, mp_arr_to_lv_area_t___32__(dest[1]), sizeof(lv_area_t)*32); break; // converting to lv_area_t [32]; + case MP_QSTR_inv_areas: mp_lv_struct_array_assign(data->inv_areas, 32, sizeof(lv_area_t), mp_write_ptr_lv_area_t, dest[1]); break; // converting to lv_area_t [32]; case MP_QSTR_inv_area_joined: memcpy((void*)&data->inv_area_joined, mp_arr_to_uint8_t___32__(dest[1]), sizeof(uint8_t)*32); break; // converting to uint8_t [32]; case MP_QSTR_inv_p: data->inv_p = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_inv_en_cnt: data->inv_en_cnt = (int32_t)mp_obj_get_int(dest[1]); break; // converting to int32_t; @@ -29826,7 +29931,7 @@ static void mp_lv_draw_triangle_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t switch(attr) { case MP_QSTR_base: dest[0] = mp_read_byref_lv_draw_dsc_base_t(data->base); break; // converting from lv_draw_dsc_base_t; - case MP_QSTR_p: dest[0] = mp_arr_from_lv_point_precise_t___3__(data->p); break; // converting from lv_point_precise_t [3]; + case MP_QSTR_p: dest[0] = mp_lv_struct_array_new(self_in, data->p, 3, sizeof(lv_point_precise_t), mp_read_ptr_lv_point_precise_t, mp_write_ptr_lv_point_precise_t); break; // converting from lv_point_precise_t [3]; case MP_QSTR_color: dest[0] = mp_read_byref_lv_color_t(data->color); break; // converting from lv_color_t; case MP_QSTR_opa: dest[0] = mp_obj_new_int_from_uint(data->opa); break; // converting from lv_opa_t; case MP_QSTR_grad: dest[0] = mp_read_byref_lv_grad_dsc_t(data->grad); break; // converting from lv_grad_dsc_t; @@ -29839,7 +29944,7 @@ static void mp_lv_draw_triangle_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t switch(attr) { case MP_QSTR_base: data->base = mp_write_lv_draw_dsc_base_t(dest[1]); break; // converting to lv_draw_dsc_base_t; - case MP_QSTR_p: memcpy((void*)&data->p, mp_arr_to_lv_point_precise_t___3__(dest[1]), sizeof(lv_point_precise_t)*3); break; // converting to lv_point_precise_t [3]; + case MP_QSTR_p: mp_lv_struct_array_assign(data->p, 3, sizeof(lv_point_precise_t), mp_write_ptr_lv_point_precise_t, dest[1]); break; // converting to lv_point_precise_t [3]; case MP_QSTR_color: data->color = mp_write_lv_color_t(dest[1]); break; // converting to lv_color_t; case MP_QSTR_opa: data->opa = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to lv_opa_t; case MP_QSTR_grad: data->grad = mp_write_lv_grad_dsc_t(dest[1]); break; // converting to lv_grad_dsc_t; @@ -30173,7 +30278,7 @@ static void mp_lv_indev_pointer_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *de case MP_QSTR_last_point: dest[0] = mp_read_byref_lv_point_t(data->last_point); break; // converting from lv_point_t; case MP_QSTR_last_raw_point: dest[0] = mp_read_byref_lv_point_t(data->last_raw_point); break; // converting from lv_point_t; case MP_QSTR_vect: dest[0] = mp_read_byref_lv_point_t(data->vect); break; // converting from lv_point_t; - case MP_QSTR_vect_hist: dest[0] = mp_arr_from_lv_point_t___8__(data->vect_hist); break; // converting from lv_point_t [8]; + case MP_QSTR_vect_hist: dest[0] = mp_lv_struct_array_new(self_in, data->vect_hist, 8, sizeof(lv_point_t), mp_read_ptr_lv_point_t, mp_write_ptr_lv_point_t); break; // converting from lv_point_t [8]; case MP_QSTR_vect_hist_timestamp: dest[0] = mp_arr_from_uint32_t___8__(data->vect_hist_timestamp); break; // converting from uint32_t [8]; case MP_QSTR_vect_hist_index: dest[0] = mp_obj_new_int_from_uint(data->vect_hist_index); break; // converting from uint8_t; case MP_QSTR_scroll_sum: dest[0] = mp_read_byref_lv_point_t(data->scroll_sum); break; // converting from lv_point_t; @@ -30206,7 +30311,7 @@ static void mp_lv_indev_pointer_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *de case MP_QSTR_last_point: data->last_point = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; case MP_QSTR_last_raw_point: data->last_raw_point = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; case MP_QSTR_vect: data->vect = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; - case MP_QSTR_vect_hist: memcpy((void*)&data->vect_hist, mp_arr_to_lv_point_t___8__(dest[1]), sizeof(lv_point_t)*8); break; // converting to lv_point_t [8]; + case MP_QSTR_vect_hist: mp_lv_struct_array_assign(data->vect_hist, 8, sizeof(lv_point_t), mp_write_ptr_lv_point_t, dest[1]); break; // converting to lv_point_t [8]; case MP_QSTR_vect_hist_timestamp: memcpy((void*)&data->vect_hist_timestamp, mp_arr_to_uint32_t___8__(dest[1]), sizeof(uint32_t)*8); break; // converting to uint32_t [8]; case MP_QSTR_vect_hist_index: data->vect_hist_index = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to uint8_t; case MP_QSTR_scroll_sum: data->scroll_sum = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; @@ -30832,7 +30937,7 @@ static void mp_lv_indev_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_event_list: dest[0] = mp_read_byref_lv_event_list_t(data->event_list); break; // converting from lv_event_list_t; case MP_QSTR_scroll_throw_anim: dest[0] = mp_read_ptr_lv_anim_t((void*)data->scroll_throw_anim); break; // converting from lv_anim_t *; case MP_QSTR_key_remap_cb: dest[0] = mp_lv_funcptr(&mp_funcptr_lv_indev_key_remap_cb_t_mpobj, data->key_remap_cb, lv_indev_t_key_remap_cb_callback ,MP_QSTR_lv_indev_t_key_remap_cb, data->user_data); break; // converting from callback lv_indev_key_remap_cb_t; - case MP_QSTR_recognizers: dest[0] = mp_arr_from_lv_indev_gesture_recognizer_t___LV_INDEV_GESTURE_CNT__(data->recognizers); break; // converting from lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; + case MP_QSTR_recognizers: dest[0] = mp_lv_struct_array_new(self_in, data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_read_ptr_lv_indev_gesture_recognizer_t, mp_write_ptr_lv_indev_gesture_recognizer_t); break; // converting from lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; case MP_QSTR_cur_gesture: dest[0] = mp_obj_new_int(data->cur_gesture); break; // converting from lv_indev_gesture_type_t; case MP_QSTR_gesture_data: dest[0] = mp_arr_from_void_ptr__LV_INDEV_GESTURE_CNT__(data->gesture_data); break; // converting from void *[LV_INDEV_GESTURE_CNT]; case MP_QSTR_gesture_type: dest[0] = mp_arr_from_lv_indev_gesture_type_t___LV_INDEV_GESTURE_CNT__(data->gesture_type); break; // converting from lv_indev_gesture_type_t [LV_INDEV_GESTURE_CNT]; @@ -30876,7 +30981,7 @@ static void mp_lv_indev_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_event_list: data->event_list = mp_write_lv_event_list_t(dest[1]); break; // converting to lv_event_list_t; case MP_QSTR_scroll_throw_anim: data->scroll_throw_anim = (void*)mp_write_ptr_lv_anim_t(dest[1]); break; // converting to lv_anim_t *; case MP_QSTR_key_remap_cb: data->key_remap_cb = mp_lv_callback(dest[1], lv_indev_t_key_remap_cb_callback ,MP_QSTR_lv_indev_t_key_remap_cb, &data->user_data, NULL, NULL, NULL); break; // converting to callback lv_indev_key_remap_cb_t; - case MP_QSTR_recognizers: memcpy((void*)&data->recognizers, mp_arr_to_lv_indev_gesture_recognizer_t___LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(lv_indev_gesture_recognizer_t)*LV_INDEV_GESTURE_CNT); break; // converting to lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; + case MP_QSTR_recognizers: mp_lv_struct_array_assign(data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_write_ptr_lv_indev_gesture_recognizer_t, dest[1]); break; // converting to lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; case MP_QSTR_cur_gesture: data->cur_gesture = (int)mp_obj_get_int(dest[1]); break; // converting to lv_indev_gesture_type_t; case MP_QSTR_gesture_data: memcpy((void*)&data->gesture_data, mp_arr_to_void_ptr__LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(void *)*LV_INDEV_GESTURE_CNT); break; // converting to void *[LV_INDEV_GESTURE_CNT]; case MP_QSTR_gesture_type: memcpy((void*)&data->gesture_type, mp_arr_to_lv_indev_gesture_type_t___LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(lv_indev_gesture_type_t)*LV_INDEV_GESTURE_CNT); break; // converting to lv_indev_gesture_type_t [LV_INDEV_GESTURE_CNT]; diff --git a/generated/lvgl_micropython.c b/generated/lvgl_micropython.c index 7886009..8c9c941 100644 --- a/generated/lvgl_micropython.c +++ b/generated/lvgl_micropython.c @@ -981,6 +981,111 @@ static MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &mp_base_struct_locals_dict ); +/* + * Fixed-size struct arrays inside a struct (lv_point_precise_t p[3]). + * + * Reading the field gives a view over the parent struct's memory, so + * d.p[1].x = 7 and d.p[1] = point write through, and len(d.p) works. The view + * holds the parent so the memory stays alive. Assigning the field takes a + * sequence of exactly that many structs (or dicts), or a packed buffer of the + * array's size; anything else raises and leaves the field unchanged + * (lvgl-bindings#38). + */ + +typedef struct { + mp_obj_base_t base; + mp_obj_t owner; + char *data; + size_t len; + size_t elem_size; + mp_obj_t (*read)(void *elem); + void *(*write)(mp_obj_t value); +} mp_lv_struct_array_t; + +static mp_obj_t lv_struct_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + if (value == MP_OBJ_NULL) { + mp_raise_TypeError(MP_ERROR_TEXT("can't delete struct array items")); + } + size_t i = mp_get_index(self->base.type, self->len, index, false); + char *elem = self->data + i * self->elem_size; + if (value == MP_OBJ_SENTINEL) { + return self->read(elem); + } + memcpy(elem, self->write(value), self->elem_size); + return mp_const_none; +} + +static mp_obj_t lv_struct_array_unary_op(mp_unary_op_t op, mp_obj_t self_in) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0); + default: return MP_OBJ_NULL; + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_lv_struct_array_type, + MP_QSTR_StructArray, + MP_TYPE_FLAG_NONE, + unary_op, lv_struct_array_unary_op, + subscr, lv_struct_array_subscr +); + +GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data, + size_t len, size_t elem_size, mp_obj_t (*read)(void *), void *(*write)(mp_obj_t)) +{ + mp_lv_struct_array_t *self = m_new_obj(mp_lv_struct_array_t); + self->base.type = &mp_lv_struct_array_type; + self->owner = owner; + self->data = (char *)data; + self->len = len; + self->elem_size = elem_size; + self->read = read; + self->write = write; + return MP_OBJ_FROM_PTR(self); +} + +GENMPY_UNUSED static void mp_lv_struct_array_assign(void *data, size_t len, + size_t elem_size, void *(*write)(mp_obj_t), mp_obj_t value) +{ + if (MP_OBJ_IS_STR_OR_BYTES(value) || + MP_OBJ_IS_TYPE(value, &mp_type_bytearray) || + MP_OBJ_IS_TYPE(value, &mp_type_memoryview)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != len * elem_size) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected a buffer of %d bytes, got %d"), + (int)(len * elem_size), (int)bufinfo.len); + } + memcpy(data, bufinfo.buf, bufinfo.len); + return; + } + mp_obj_t mp_len = mp_obj_len_maybe(value); + if (mp_len == MP_OBJ_NULL) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("expected a sequence of %d structs, got '%s'"), + (int)len, mp_obj_get_type_str(value)); + } + if ((size_t)mp_obj_get_int(mp_len) != len) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("expected %d items, got %d"), + (int)len, (int)mp_obj_get_int(mp_len)); + } + // Convert every item before writing any, so a bad item changes nothing. + char *tmp = m_new(char, len * elem_size); + for (size_t i = 0; i < len; i++) { + mp_obj_t item = mp_obj_subscr(value, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL); + memcpy(tmp + i * elem_size, write(item), elem_size); + } + memcpy(data, tmp, len * elem_size); + m_del(char, tmp, len * elem_size); +} + GENMPY_UNUSED static mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) { mp_lv_array_t *self = m_new_obj(mp_lv_array_t); @@ -6915,7 +7020,7 @@ static void mp_lv_grad_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) // load attribute switch(attr) { - case MP_QSTR_stops: dest[0] = mp_arr_from_lv_grad_stop_t___2__(data->stops); break; // converting from lv_grad_stop_t [2]; + case MP_QSTR_stops: dest[0] = mp_lv_struct_array_new(self_in, data->stops, 2, sizeof(lv_grad_stop_t), mp_read_ptr_lv_grad_stop_t, mp_write_ptr_lv_grad_stop_t); break; // converting from lv_grad_stop_t [2]; case MP_QSTR_stops_count: dest[0] = mp_obj_new_int_from_uint(data->stops_count); break; // converting from uint8_t; case MP_QSTR_dir: dest[0] = mp_obj_new_int(data->dir); break; // converting from lv_grad_dir_t; case MP_QSTR_extend: dest[0] = mp_obj_new_int(data->extend); break; // converting from lv_grad_extend_t; @@ -6929,7 +7034,7 @@ static void mp_lv_grad_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) // store attribute switch(attr) { - case MP_QSTR_stops: memcpy((void*)&data->stops, mp_arr_to_lv_grad_stop_t___2__(dest[1]), sizeof(lv_grad_stop_t)*2); break; // converting to lv_grad_stop_t [2]; + case MP_QSTR_stops: mp_lv_struct_array_assign(data->stops, 2, sizeof(lv_grad_stop_t), mp_write_ptr_lv_grad_stop_t, dest[1]); break; // converting to lv_grad_stop_t [2]; case MP_QSTR_stops_count: data->stops_count = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to uint8_t; case MP_QSTR_dir: data->dir = (int)mp_obj_get_int(dest[1]); break; // converting to lv_grad_dir_t; case MP_QSTR_extend: data->extend = (int)mp_obj_get_int(dest[1]); break; // converting to lv_grad_extend_t; @@ -11636,7 +11741,7 @@ static void mp_lv_display_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_stride_is_auto: dest[0] = mp_obj_new_int_from_uint(data->stride_is_auto); break; // converting from uint32_t; case MP_QSTR_rendering_in_progress: dest[0] = mp_obj_new_int_from_uint(data->rendering_in_progress); break; // converting from uint32_t; case MP_QSTR_color_format: dest[0] = mp_obj_new_int(data->color_format); break; // converting from lv_color_format_t; - case MP_QSTR_inv_areas: dest[0] = mp_arr_from_lv_area_t___32__(data->inv_areas); break; // converting from lv_area_t [32]; + case MP_QSTR_inv_areas: dest[0] = mp_lv_struct_array_new(self_in, data->inv_areas, 32, sizeof(lv_area_t), mp_read_ptr_lv_area_t, mp_write_ptr_lv_area_t); break; // converting from lv_area_t [32]; case MP_QSTR_inv_area_joined: dest[0] = mp_arr_from_uint8_t___32__(data->inv_area_joined); break; // converting from uint8_t [32]; case MP_QSTR_inv_p: dest[0] = mp_obj_new_int_from_uint(data->inv_p); break; // converting from uint32_t; case MP_QSTR_inv_en_cnt: dest[0] = mp_obj_new_int(data->inv_en_cnt); break; // converting from int32_t; @@ -11697,7 +11802,7 @@ static void mp_lv_display_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_stride_is_auto: data->stride_is_auto = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_rendering_in_progress: data->rendering_in_progress = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_color_format: data->color_format = (int)mp_obj_get_int(dest[1]); break; // converting to lv_color_format_t; - case MP_QSTR_inv_areas: memcpy((void*)&data->inv_areas, mp_arr_to_lv_area_t___32__(dest[1]), sizeof(lv_area_t)*32); break; // converting to lv_area_t [32]; + case MP_QSTR_inv_areas: mp_lv_struct_array_assign(data->inv_areas, 32, sizeof(lv_area_t), mp_write_ptr_lv_area_t, dest[1]); break; // converting to lv_area_t [32]; case MP_QSTR_inv_area_joined: memcpy((void*)&data->inv_area_joined, mp_arr_to_uint8_t___32__(dest[1]), sizeof(uint8_t)*32); break; // converting to uint8_t [32]; case MP_QSTR_inv_p: data->inv_p = (uint32_t)mp_obj_get_int(dest[1]); break; // converting to uint32_t; case MP_QSTR_inv_en_cnt: data->inv_en_cnt = (int32_t)mp_obj_get_int(dest[1]); break; // converting to int32_t; @@ -30498,7 +30603,7 @@ static void mp_lv_draw_triangle_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t switch(attr) { case MP_QSTR_base: dest[0] = mp_read_byref_lv_draw_dsc_base_t(data->base); break; // converting from lv_draw_dsc_base_t; - case MP_QSTR_p: dest[0] = mp_arr_from_lv_point_precise_t___3__(data->p); break; // converting from lv_point_precise_t [3]; + case MP_QSTR_p: dest[0] = mp_lv_struct_array_new(self_in, data->p, 3, sizeof(lv_point_precise_t), mp_read_ptr_lv_point_precise_t, mp_write_ptr_lv_point_precise_t); break; // converting from lv_point_precise_t [3]; case MP_QSTR_color: dest[0] = mp_read_byref_lv_color_t(data->color); break; // converting from lv_color_t; case MP_QSTR_opa: dest[0] = mp_obj_new_int_from_uint(data->opa); break; // converting from lv_opa_t; case MP_QSTR_grad: dest[0] = mp_read_byref_lv_grad_dsc_t(data->grad); break; // converting from lv_grad_dsc_t; @@ -30511,7 +30616,7 @@ static void mp_lv_draw_triangle_dsc_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t switch(attr) { case MP_QSTR_base: data->base = mp_write_lv_draw_dsc_base_t(dest[1]); break; // converting to lv_draw_dsc_base_t; - case MP_QSTR_p: memcpy((void*)&data->p, mp_arr_to_lv_point_precise_t___3__(dest[1]), sizeof(lv_point_precise_t)*3); break; // converting to lv_point_precise_t [3]; + case MP_QSTR_p: mp_lv_struct_array_assign(data->p, 3, sizeof(lv_point_precise_t), mp_write_ptr_lv_point_precise_t, dest[1]); break; // converting to lv_point_precise_t [3]; case MP_QSTR_color: data->color = mp_write_lv_color_t(dest[1]); break; // converting to lv_color_t; case MP_QSTR_opa: data->opa = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to lv_opa_t; case MP_QSTR_grad: data->grad = mp_write_lv_grad_dsc_t(dest[1]); break; // converting to lv_grad_dsc_t; @@ -30845,7 +30950,7 @@ static void mp_lv_indev_pointer_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *de case MP_QSTR_last_point: dest[0] = mp_read_byref_lv_point_t(data->last_point); break; // converting from lv_point_t; case MP_QSTR_last_raw_point: dest[0] = mp_read_byref_lv_point_t(data->last_raw_point); break; // converting from lv_point_t; case MP_QSTR_vect: dest[0] = mp_read_byref_lv_point_t(data->vect); break; // converting from lv_point_t; - case MP_QSTR_vect_hist: dest[0] = mp_arr_from_lv_point_t___8__(data->vect_hist); break; // converting from lv_point_t [8]; + case MP_QSTR_vect_hist: dest[0] = mp_lv_struct_array_new(self_in, data->vect_hist, 8, sizeof(lv_point_t), mp_read_ptr_lv_point_t, mp_write_ptr_lv_point_t); break; // converting from lv_point_t [8]; case MP_QSTR_vect_hist_timestamp: dest[0] = mp_arr_from_uint32_t___8__(data->vect_hist_timestamp); break; // converting from uint32_t [8]; case MP_QSTR_vect_hist_index: dest[0] = mp_obj_new_int_from_uint(data->vect_hist_index); break; // converting from uint8_t; case MP_QSTR_scroll_sum: dest[0] = mp_read_byref_lv_point_t(data->scroll_sum); break; // converting from lv_point_t; @@ -30878,7 +30983,7 @@ static void mp_lv_indev_pointer_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *de case MP_QSTR_last_point: data->last_point = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; case MP_QSTR_last_raw_point: data->last_raw_point = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; case MP_QSTR_vect: data->vect = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; - case MP_QSTR_vect_hist: memcpy((void*)&data->vect_hist, mp_arr_to_lv_point_t___8__(dest[1]), sizeof(lv_point_t)*8); break; // converting to lv_point_t [8]; + case MP_QSTR_vect_hist: mp_lv_struct_array_assign(data->vect_hist, 8, sizeof(lv_point_t), mp_write_ptr_lv_point_t, dest[1]); break; // converting to lv_point_t [8]; case MP_QSTR_vect_hist_timestamp: memcpy((void*)&data->vect_hist_timestamp, mp_arr_to_uint32_t___8__(dest[1]), sizeof(uint32_t)*8); break; // converting to uint32_t [8]; case MP_QSTR_vect_hist_index: data->vect_hist_index = (uint8_t)mp_obj_get_int(dest[1]); break; // converting to uint8_t; case MP_QSTR_scroll_sum: data->scroll_sum = mp_write_lv_point_t(dest[1]); break; // converting to lv_point_t; @@ -31504,7 +31609,7 @@ static void mp_lv_indev_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_event_list: dest[0] = mp_read_byref_lv_event_list_t(data->event_list); break; // converting from lv_event_list_t; case MP_QSTR_scroll_throw_anim: dest[0] = mp_read_ptr_lv_anim_t((void*)data->scroll_throw_anim); break; // converting from lv_anim_t *; case MP_QSTR_key_remap_cb: dest[0] = mp_lv_funcptr(&mp_funcptr_lv_indev_key_remap_cb_t_mpobj, data->key_remap_cb, lv_indev_t_key_remap_cb_callback ,MP_QSTR_lv_indev_t_key_remap_cb, data->user_data); break; // converting from callback lv_indev_key_remap_cb_t; - case MP_QSTR_recognizers: dest[0] = mp_arr_from_lv_indev_gesture_recognizer_t___LV_INDEV_GESTURE_CNT__(data->recognizers); break; // converting from lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; + case MP_QSTR_recognizers: dest[0] = mp_lv_struct_array_new(self_in, data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_read_ptr_lv_indev_gesture_recognizer_t, mp_write_ptr_lv_indev_gesture_recognizer_t); break; // converting from lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; case MP_QSTR_cur_gesture: dest[0] = mp_obj_new_int(data->cur_gesture); break; // converting from lv_indev_gesture_type_t; case MP_QSTR_gesture_data: dest[0] = mp_arr_from_void_ptr__LV_INDEV_GESTURE_CNT__(data->gesture_data); break; // converting from void *[LV_INDEV_GESTURE_CNT]; case MP_QSTR_gesture_type: dest[0] = mp_arr_from_lv_indev_gesture_type_t___LV_INDEV_GESTURE_CNT__(data->gesture_type); break; // converting from lv_indev_gesture_type_t [LV_INDEV_GESTURE_CNT]; @@ -31548,7 +31653,7 @@ static void mp_lv_indev_t_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) case MP_QSTR_event_list: data->event_list = mp_write_lv_event_list_t(dest[1]); break; // converting to lv_event_list_t; case MP_QSTR_scroll_throw_anim: data->scroll_throw_anim = (void*)mp_write_ptr_lv_anim_t(dest[1]); break; // converting to lv_anim_t *; case MP_QSTR_key_remap_cb: data->key_remap_cb = mp_lv_callback(dest[1], lv_indev_t_key_remap_cb_callback ,MP_QSTR_lv_indev_t_key_remap_cb, &data->user_data, NULL, NULL, NULL); break; // converting to callback lv_indev_key_remap_cb_t; - case MP_QSTR_recognizers: memcpy((void*)&data->recognizers, mp_arr_to_lv_indev_gesture_recognizer_t___LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(lv_indev_gesture_recognizer_t)*LV_INDEV_GESTURE_CNT); break; // converting to lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; + case MP_QSTR_recognizers: mp_lv_struct_array_assign(data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_write_ptr_lv_indev_gesture_recognizer_t, dest[1]); break; // converting to lv_indev_gesture_recognizer_t [LV_INDEV_GESTURE_CNT]; case MP_QSTR_cur_gesture: data->cur_gesture = (int)mp_obj_get_int(dest[1]); break; // converting to lv_indev_gesture_type_t; case MP_QSTR_gesture_data: memcpy((void*)&data->gesture_data, mp_arr_to_void_ptr__LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(void *)*LV_INDEV_GESTURE_CNT); break; // converting to void *[LV_INDEV_GESTURE_CNT]; case MP_QSTR_gesture_type: memcpy((void*)&data->gesture_type, mp_arr_to_lv_indev_gesture_type_t___LV_INDEV_GESTURE_CNT__(dest[1]), sizeof(lv_indev_gesture_type_t)*LV_INDEV_GESTURE_CNT); break; // converting to lv_indev_gesture_type_t [LV_INDEV_GESTURE_CNT]; diff --git a/generated/lvgl_python.c b/generated/lvgl_python.c index bf155fc..c231c7e 100644 --- a/generated/lvgl_python.c +++ b/generated/lvgl_python.c @@ -154,6 +154,142 @@ static void *lvpy_struct_arr_arg(PyObject *self, PyObject *arg, const char *key, return arr; } +/* + * Fixed-size struct arrays inside a struct (lv_point_precise_t p[3]). + * + * Reading the field gives a view over the parent struct's memory, so + * d.p[1].x = 7 and d.p[1] = point write through, and len(d.p) works. The view + * holds the parent so the memory stays alive. Assigning the field takes a + * sequence of exactly that many structs (or dicts), or a packed buffer of the + * array's size; anything else raises and leaves the field unchanged + * (lvgl-bindings#38). + */ + +typedef PyObject *(*lvpy_struct_reader_t)(void *); + +typedef struct { + PyObject_HEAD + PyObject *owner; + char *data; + Py_ssize_t len; + size_t elem_size; + lvpy_struct_reader_t read; + lvpy_struct_writer_t write; +} lvpy_struct_array_t; + +static void lvpy_struct_array_dealloc(PyObject *self) +{ + Py_XDECREF(((lvpy_struct_array_t *)self)->owner); + Py_TYPE(self)->tp_free(self); +} + +static Py_ssize_t lvpy_struct_array_len(PyObject *self) +{ + return ((lvpy_struct_array_t *)self)->len; +} + +static PyObject *lvpy_struct_array_item(PyObject *self_in, Py_ssize_t i) +{ + lvpy_struct_array_t *self = (lvpy_struct_array_t *)self_in; + if (i < 0 || i >= self->len) { + PyErr_SetString(PyExc_IndexError, "struct array index out of range"); + return NULL; + } + return self->read(self->data + (size_t)i * self->elem_size); +} + +static int lvpy_struct_array_ass_item(PyObject *self_in, Py_ssize_t i, PyObject *value) +{ + lvpy_struct_array_t *self = (lvpy_struct_array_t *)self_in; + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "can't delete struct array items"); + return -1; + } + if (i < 0 || i >= self->len) { + PyErr_SetString(PyExc_IndexError, "struct array assignment index out of range"); + return -1; + } + void *p = self->write(value); + if (PyErr_Occurred()) return -1; + memcpy(self->data + (size_t)i * self->elem_size, p, self->elem_size); + return 0; +} + +static PySequenceMethods lvpy_struct_array_as_sequence = { + .sq_length = lvpy_struct_array_len, + .sq_item = lvpy_struct_array_item, + .sq_ass_item = lvpy_struct_array_ass_item, +}; + +static PyTypeObject lvpy_struct_array_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "lvgl.StructArray", + .tp_basicsize = sizeof(lvpy_struct_array_t), + .tp_dealloc = lvpy_struct_array_dealloc, + .tp_as_sequence = &lvpy_struct_array_as_sequence, + .tp_flags = Py_TPFLAGS_DEFAULT, +}; + +static inline PyObject *lvpy_struct_array_new(PyObject *owner, void *data, + Py_ssize_t len, size_t elem_size, lvpy_struct_reader_t read, lvpy_struct_writer_t write) +{ + if (!(lvpy_struct_array_type.tp_flags & Py_TPFLAGS_READY) && + PyType_Ready(&lvpy_struct_array_type) < 0) + return NULL; + lvpy_struct_array_t *self = PyObject_New(lvpy_struct_array_t, &lvpy_struct_array_type); + if (!self) return NULL; + Py_XINCREF(owner); + self->owner = owner; + self->data = (char *)data; + self->len = len; + self->elem_size = elem_size; + self->read = read; + self->write = write; + return (PyObject *)self; +} + +/* Sets an exception and leaves data unchanged on any mismatch. */ +static inline void lvpy_struct_array_assign(void *data, Py_ssize_t len, + size_t elem_size, lvpy_struct_writer_t write, PyObject *value) +{ + size_t total = (size_t)len * elem_size; + if (PyBytes_Check(value) || PyByteArray_Check(value) || PyMemoryView_Check(value)) { + Py_buffer view; + if (PyObject_GetBuffer(value, &view, PyBUF_C_CONTIGUOUS) < 0) return; + if ((size_t)view.len != total) + PyErr_Format(PyExc_ValueError, "expected a buffer of %zu bytes, got %zd", + total, view.len); + else + memcpy(data, view.buf, total); + PyBuffer_Release(&view); + return; + } + if (!PySequence_Check(value) || PyUnicode_Check(value)) { + PyErr_Format(PyExc_TypeError, "expected a sequence of %zd structs, got '%s'", + len, Py_TYPE(value)->tp_name); + return; + } + PyObject *items = PySequence_Tuple(value); + if (!items) return; + if (PyTuple_GET_SIZE(items) != len) { + PyErr_Format(PyExc_ValueError, "expected %zd items, got %zd", + len, PyTuple_GET_SIZE(items)); + Py_DECREF(items); + return; + } + /* Convert every item before writing any, so a bad item changes nothing. */ + char *tmp = (char *)PyMem_Malloc(total ? total : 1); + if (!tmp) { Py_DECREF(items); PyErr_NoMemory(); return; } + for (Py_ssize_t i = 0; i < len; i++) { + void *p = write(PyTuple_GET_ITEM(items, i)); + if (PyErr_Occurred()) { PyMem_Free(tmp); Py_DECREF(items); return; } + memcpy(tmp + (size_t)i * elem_size, p, elem_size); + } + memcpy(data, tmp, total); + PyMem_Free(tmp); + Py_DECREF(items); +} + /* * LVGL string constants @@ -7556,7 +7692,7 @@ static PyObject *py_lv_grad_dsc_t_getattro(PyObject *self, PyObject *name) } const char *attr = PyUnicode_AsUTF8(name); if (attr == NULL) return NULL; - if (strcmp(attr, "stops") == 0) return ptr_to_mp(data->stops); + if (strcmp(attr, "stops") == 0) return lvpy_struct_array_new(self, data->stops, 2, sizeof(lv_grad_stop_t), mp_read_ptr_lv_grad_stop_t, mp_write_value_ptr_lv_grad_stop_t); if (strcmp(attr, "stops_count") == 0) return mp_obj_new_int_from_uint(data->stops_count); if (strcmp(attr, "dir") == 0) return mp_obj_new_int(data->dir); if (strcmp(attr, "extend") == 0) return mp_obj_new_int(data->extend); @@ -7585,7 +7721,7 @@ static int py_lv_grad_dsc_t_setattro(PyObject *self, PyObject *name, PyObject *v int result = -1; lv_grad_dsc_t saved; memcpy(&saved, data, sizeof(saved)); - if (strcmp(attr, "stops") == 0) { memcpy((void*)&data->stops, mp_to_ptr(value), sizeof(lv_grad_stop_t)*2); result = 0; } + if (strcmp(attr, "stops") == 0) { lvpy_struct_array_assign(data->stops, 2, sizeof(lv_grad_stop_t), mp_write_value_ptr_lv_grad_stop_t, value); result = 0; } if (strcmp(attr, "stops_count") == 0) { data->stops_count = (uint8_t)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "dir") == 0) { data->dir = (int)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "extend") == 0) { data->extend = (int)mp_obj_get_int(value); result = 0; } @@ -14241,7 +14377,7 @@ static PyObject *py_lv_display_t_getattro(PyObject *self, PyObject *name) if (strcmp(attr, "stride_is_auto") == 0) return mp_obj_new_int_from_uint(data->stride_is_auto); if (strcmp(attr, "rendering_in_progress") == 0) return mp_obj_new_int_from_uint(data->rendering_in_progress); if (strcmp(attr, "color_format") == 0) return mp_obj_new_int(data->color_format); - if (strcmp(attr, "inv_areas") == 0) return ptr_to_mp(data->inv_areas); + if (strcmp(attr, "inv_areas") == 0) return lvpy_struct_array_new(self, data->inv_areas, 32, sizeof(lv_area_t), mp_read_ptr_lv_area_t, mp_write_value_ptr_lv_area_t); if (strcmp(attr, "inv_area_joined") == 0) return ptr_to_mp(data->inv_area_joined); if (strcmp(attr, "inv_p") == 0) return mp_obj_new_int_from_uint(data->inv_p); if (strcmp(attr, "inv_en_cnt") == 0) return mp_obj_new_int(data->inv_en_cnt); @@ -14318,7 +14454,7 @@ static int py_lv_display_t_setattro(PyObject *self, PyObject *name, PyObject *va if (strcmp(attr, "stride_is_auto") == 0) { data->stride_is_auto = (uint32_t)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "rendering_in_progress") == 0) { data->rendering_in_progress = (uint32_t)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "color_format") == 0) { data->color_format = (int)mp_obj_get_int(value); result = 0; } - if (strcmp(attr, "inv_areas") == 0) { memcpy((void*)&data->inv_areas, mp_to_ptr(value), sizeof(lv_area_t)*32); result = 0; } + if (strcmp(attr, "inv_areas") == 0) { lvpy_struct_array_assign(data->inv_areas, 32, sizeof(lv_area_t), mp_write_value_ptr_lv_area_t, value); result = 0; } if (strcmp(attr, "inv_area_joined") == 0) { memcpy((void*)&data->inv_area_joined, mp_to_ptr(value), sizeof(uint8_t)*32); result = 0; } if (strcmp(attr, "inv_p") == 0) { data->inv_p = (uint32_t)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "inv_en_cnt") == 0) { data->inv_en_cnt = (int32_t)mp_obj_get_int(value); result = 0; } @@ -53988,7 +54124,7 @@ static PyObject *py_lv_draw_triangle_dsc_t_getattro(PyObject *self, PyObject *na const char *attr = PyUnicode_AsUTF8(name); if (attr == NULL) return NULL; if (strcmp(attr, "base") == 0) return mp_read_byref_lv_draw_dsc_base_t(data->base); - if (strcmp(attr, "p") == 0) return ptr_to_mp(data->p); + if (strcmp(attr, "p") == 0) return lvpy_struct_array_new(self, data->p, 3, sizeof(lv_point_precise_t), mp_read_ptr_lv_point_precise_t, mp_write_value_ptr_lv_point_precise_t); if (strcmp(attr, "color") == 0) return mp_read_byref_lv_color_t(data->color); if (strcmp(attr, "opa") == 0) return mp_obj_new_int_from_uint(data->opa); if (strcmp(attr, "grad") == 0) return mp_read_byref_lv_grad_dsc_t(data->grad); @@ -54017,7 +54153,7 @@ static int py_lv_draw_triangle_dsc_t_setattro(PyObject *self, PyObject *name, Py lv_draw_triangle_dsc_t saved; memcpy(&saved, data, sizeof(saved)); if (strcmp(attr, "base") == 0) { data->base = mp_write_lv_draw_dsc_base_t(value); result = 0; } - if (strcmp(attr, "p") == 0) { memcpy((void*)&data->p, mp_to_ptr(value), sizeof(lv_point_precise_t)*3); result = 0; } + if (strcmp(attr, "p") == 0) { lvpy_struct_array_assign(data->p, 3, sizeof(lv_point_precise_t), mp_write_value_ptr_lv_point_precise_t, value); result = 0; } if (strcmp(attr, "color") == 0) { data->color = mp_write_lv_color_t(value); result = 0; } if (strcmp(attr, "opa") == 0) { data->opa = (uint8_t)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "grad") == 0) { data->grad = mp_write_lv_grad_dsc_t(value); result = 0; } @@ -54488,7 +54624,7 @@ static PyObject *py_lv_indev_t_getattro(PyObject *self, PyObject *name) if (strcmp(attr, "event_list") == 0) return mp_read_byref_lv_event_list_t(data->event_list); if (strcmp(attr, "scroll_throw_anim") == 0) return mp_read_ptr_lv_anim_t((void*)data->scroll_throw_anim); if (strcmp(attr, "key_remap_cb") == 0) return mp_lv_funcptr(NULL, data->key_remap_cb, lv_indev_t_key_remap_cb_callback, "lv_indev_t_key_remap_cb", data->user_data); - if (strcmp(attr, "recognizers") == 0) return ptr_to_mp(data->recognizers); + if (strcmp(attr, "recognizers") == 0) return lvpy_struct_array_new(self, data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_read_ptr_lv_indev_gesture_recognizer_t, mp_write_value_ptr_lv_indev_gesture_recognizer_t); if (strcmp(attr, "cur_gesture") == 0) return mp_obj_new_int(data->cur_gesture); if (strcmp(attr, "gesture_data") == 0) return ptr_to_mp(data->gesture_data); if (strcmp(attr, "gesture_type") == 0) return ptr_to_mp(data->gesture_type); @@ -54546,7 +54682,7 @@ static int py_lv_indev_t_setattro(PyObject *self, PyObject *name, PyObject *valu if (strcmp(attr, "event_list") == 0) { data->event_list = mp_write_lv_event_list_t(value); result = 0; } if (strcmp(attr, "scroll_throw_anim") == 0) { data->scroll_throw_anim = (void*)mp_write_ptr_lv_anim_t(value); result = 0; } if (strcmp(attr, "key_remap_cb") == 0) { data->key_remap_cb = mp_lv_callback(value, lv_indev_t_key_remap_cb_callback, "lv_indev_t_key_remap_cb", &data->user_data, data, NULL, NULL); result = 0; } - if (strcmp(attr, "recognizers") == 0) { memcpy((void*)&data->recognizers, mp_to_ptr(value), sizeof(lv_indev_gesture_recognizer_t)*LV_INDEV_GESTURE_CNT); result = 0; } + if (strcmp(attr, "recognizers") == 0) { lvpy_struct_array_assign(data->recognizers, LV_INDEV_GESTURE_CNT, sizeof(lv_indev_gesture_recognizer_t), mp_write_value_ptr_lv_indev_gesture_recognizer_t, value); result = 0; } if (strcmp(attr, "cur_gesture") == 0) { data->cur_gesture = (int)mp_obj_get_int(value); result = 0; } if (strcmp(attr, "gesture_data") == 0) { memcpy((void*)&data->gesture_data, mp_to_ptr(value), sizeof(void *)*LV_INDEV_GESTURE_CNT); result = 0; } if (strcmp(attr, "gesture_type") == 0) { memcpy((void*)&data->gesture_type, mp_to_ptr(value), sizeof(lv_indev_gesture_type_t)*LV_INDEV_GESTURE_CNT); result = 0; } diff --git a/tools/test_lvgl_smoke.py b/tools/test_lvgl_smoke.py index 3680f65..ca09dbe 100755 --- a/tools/test_lvgl_smoke.py +++ b/tools/test_lvgl_smoke.py @@ -546,6 +546,56 @@ def test_line_points_list_and_buffer(lv): print("OK: line.set_points takes a list of points or a packed buffer") +def test_struct_array_fields(lv): + # A fixed-size struct array inside a struct (lv_point_precise_t p[3]) reads + # as a view: index reads and writes go through to the struct, len() works, + # and a wrong-sized or wrong-typed assignment raises and changes nothing + # (lvgl-bindings#38). + point_type = _lv_export(lv, "point_precise_t") + d = _lv_export(lv, "draw_triangle_dsc_t")() + d.init() + if len(d.p) != 3: + _fail("len(draw_triangle_dsc_t.p) is %r, not 3" % (len(d.p),)) + d.p[0].x = 5 + d.p[1] = point_type({"x": 7, "y": 8}) + d.p[2] = {"x": 9, "y": 10} + got = [(p.x, p.y) for p in d.p] + if got != [(5, 0), (7, 8), (9, 10)]: + _fail("struct array item writes were lost: %r" % (got,)) + d.p = [{"x": 20, "y": 200}, point_type({"x": 180, "y": 20}), {"x": 340, "y": 200}] + want = [(20, 200), (180, 20), (340, 200)] + if [(p.x, p.y) for p in d.p] != want: + _fail("whole struct array assignment was lost") + for bad in ([{"x": 1, "y": 1}], [{"x": 1}, {"x": 2}, 5], 5, None): + try: + d.p = bad + except (TypeError, ValueError, SyntaxError): + pass + else: + _fail("struct array accepted %r" % (bad,)) + if [(p.x, p.y) for p in d.p] != want: + _fail("a rejected struct array assignment changed the field: %r" % (bad,)) + try: + d.p[3] + except IndexError: + pass + else: + _fail("struct array index 3 of 3 did not raise IndexError") + # The view keeps the struct it came from alive. + view = d.p + d = None + gc.collect() + if (view[2].x, view[2].y) != (340, 200): + _fail("struct array view lost its struct") + + g = _lv_export(lv, "grad_dsc_t")() + g.stops[1].frac = 200 + g.stops[0] = {"frac": 10, "opa": 255} + if (len(g.stops), g.stops[0].frac, g.stops[1].frac) != (2, 10, 200): + _fail("grad_dsc_t.stops writes were lost") + print("OK: struct array fields index, assign and check their length") + + def test_struct_value_from_dict(lv): # A struct-by-value field takes a dict, as its type's constructor does, and # a wrong type raises instead of crashing CPython (lvgl-bindings#21). @@ -642,6 +692,7 @@ def main(): test_widget(lv) test_struct_fields_and_arrays(lv) test_line_points_list_and_buffer(lv) + test_struct_array_fields(lv) test_struct_value_from_dict(lv) test_event_callback(lv) test_callback_gc_with_widget_ref(lv) From 0d95fa2f6c85642de5d87bf872797f6433696c97 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:52:13 -0500 Subject: [PATCH 2/2] StructArray iterates on MicroPython and CircuitPython --- binding/emit_c_micropython_style.py | 18 ++++++++++++++++-- generated/lvgl_circuitpython.c | 18 ++++++++++++++++-- generated/lvgl_micropython.c | 18 ++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/binding/emit_c_micropython_style.py b/binding/emit_c_micropython_style.py index 394c691..2475bd8 100644 --- a/binding/emit_c_micropython_style.py +++ b/binding/emit_c_micropython_style.py @@ -154,6 +154,7 @@ def emit_c(ctx): #include "py/obj.h" #include "py/objint.h" #include "py/objstr.h" +#include "py/objtuple.h" #include "py/runtime.h" #include "py/binary.h" #include "py/objarray.h" @@ -1177,12 +1178,25 @@ def emit_c(ctx): } } +// Iterates views of the elements, the same objects indexing returns. +static mp_obj_t lv_struct_array_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t items = mp_obj_new_tuple(self->len, NULL); + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(items); + for (size_t i = 0; i < self->len; i++) { + t->items[i] = self->read(self->data + i * self->elem_size); + } + return mp_getiter(items, iter_buf); +} + static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_struct_array_type, MP_QSTR_StructArray, - MP_TYPE_FLAG_NONE, + MP_TYPE_FLAG_ITER_IS_GETITER, unary_op, lv_struct_array_unary_op, - subscr, lv_struct_array_subscr + subscr, lv_struct_array_subscr, + iter, lv_struct_array_getiter ); GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data, diff --git a/generated/lvgl_circuitpython.c b/generated/lvgl_circuitpython.c index 0581f78..66b4155 100644 --- a/generated/lvgl_circuitpython.c +++ b/generated/lvgl_circuitpython.c @@ -22,6 +22,7 @@ #include "py/obj.h" #include "py/objint.h" #include "py/objstr.h" +#include "py/objtuple.h" #include "py/runtime.h" #include "py/binary.h" #include "py/objarray.h" @@ -1029,12 +1030,25 @@ static mp_obj_t lv_struct_array_unary_op(mp_unary_op_t op, mp_obj_t self_in) } } +// Iterates views of the elements, the same objects indexing returns. +static mp_obj_t lv_struct_array_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t items = mp_obj_new_tuple(self->len, NULL); + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(items); + for (size_t i = 0; i < self->len; i++) { + t->items[i] = self->read(self->data + i * self->elem_size); + } + return mp_getiter(items, iter_buf); +} + static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_struct_array_type, MP_QSTR_StructArray, - MP_TYPE_FLAG_NONE, + MP_TYPE_FLAG_ITER_IS_GETITER, unary_op, lv_struct_array_unary_op, - subscr, lv_struct_array_subscr + subscr, lv_struct_array_subscr, + iter, lv_struct_array_getiter ); GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data, diff --git a/generated/lvgl_micropython.c b/generated/lvgl_micropython.c index 8c9c941..67a2808 100644 --- a/generated/lvgl_micropython.c +++ b/generated/lvgl_micropython.c @@ -20,6 +20,7 @@ #include "py/obj.h" #include "py/objint.h" #include "py/objstr.h" +#include "py/objtuple.h" #include "py/runtime.h" #include "py/binary.h" #include "py/objarray.h" @@ -1027,12 +1028,25 @@ static mp_obj_t lv_struct_array_unary_op(mp_unary_op_t op, mp_obj_t self_in) } } +// Iterates views of the elements, the same objects indexing returns. +static mp_obj_t lv_struct_array_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) +{ + mp_lv_struct_array_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t items = mp_obj_new_tuple(self->len, NULL); + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(items); + for (size_t i = 0; i < self->len; i++) { + t->items[i] = self->read(self->data + i * self->elem_size); + } + return mp_getiter(items, iter_buf); +} + static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_struct_array_type, MP_QSTR_StructArray, - MP_TYPE_FLAG_NONE, + MP_TYPE_FLAG_ITER_IS_GETITER, unary_op, lv_struct_array_unary_op, - subscr, lv_struct_array_subscr + subscr, lv_struct_array_subscr, + iter, lv_struct_array_getiter ); GENMPY_UNUSED static mp_obj_t mp_lv_struct_array_new(mp_obj_t owner, void *data,