/* * lws abstract display * * Copyright (C) 2019 - 2022 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Display List Object handling */ #include #include "private-lib-drivers-display-dlo.h" #define dlodump_loglevel LLL_NOTICE #if (_LWS_ENABLED_LOGS & dlodump_loglevel) #define lwsl_dlodump(...) _lws_log(dlodump_loglevel, __VA_ARGS__) #else #define lwsl_dlodump(...) #endif void lws_display_dl_init(lws_displaylist_t *dl, lws_display_state_t *ds) { lws_dll2_owner_clear(&dl->dl); dl->ds = ds; } int lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo) { if (!dlo_parent && !dl->dl.head) { lws_dll2_add_tail(&dlo->list, &dl->dl); return 0; } if (!dlo_parent) { if (!dl->dl.head) return 0; dlo_parent = lws_container_of(dl->dl.head, lws_dlo_t, list); } lws_dll2_add_tail(&dlo->list, &dlo_parent->children); return 0; } void lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x, const lws_display_colour_t *c) { unsigned int alpha, ialpha; lws_display_colour_t oc; lws_display_colour_t y; uint8_t rgb[3]; if (x < 0 || x >= ic->wh_px[0].whole) return; /* * All alpha composition takes place at 8bpp grey or 24bpp */ if (ic->greyscale) { /* line composition buffer is 8-bit Y per pixel */ oc = line[x]; alpha = LWSDC_ALPHA(*c); ialpha = 255 - alpha; y = RGB_TO_Y(LWSDC_R(*c), LWSDC_G(*c), LWSDC_B(*c)); line[x] = (uint8_t)(((y * alpha) / 255) + ((LWSDC_R(oc) * ialpha) / 255)); return; } /* line composition buffer is 24-bit RGB per pixel */ line += (ic->render_to_rgba ? 4 : 3) * x; alpha = LWSDC_ALPHA(*c); ialpha = 255 - alpha; rgb[0] = (uint8_t)(((LWSDC_R(*c) * alpha) / 255) + ((line[0] * ialpha) / 255)); rgb[1] = (uint8_t)(((LWSDC_G(*c) * alpha) / 255) + ((line[1] * ialpha) / 255)); rgb[2] = (uint8_t)(((LWSDC_B(*c) * alpha) / 255) + ((line[2] * ialpha) / 255)); *line++ = rgb[0]; *line++ = rgb[1]; *line++ = rgb[2]; if (ic->render_to_rgba) *line = 0xff; } /* * Recursively find out the total width and height of the contents of a DLO */ void lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim) { lws_display_render_stack_t st[12]; /* DLO child stack */ lws_dll2_t *d; lws_fx_t t1; int sp = 0; dim->w.whole = 0; dim->w.frac = 0; dim->h.whole = 0; dim->h.frac = 0; if (!parent) return; d = lws_dll2_get_head(&parent->children); if (!d) return; memset(&st, 0, sizeof(st)); st[0].dlo = lws_container_of(d, lws_dlo_t, list); st[0].co.w.whole = 0; st[0].co.w.frac = 0; st[0].co.h.whole = 0; st[0].co.h.frac = 0; /* We are collecting worst dlo->box.x + dlo->box.w and .y + .h */ while (sp || st[0].dlo) { lws_dlo_t *dlo = st[sp].dlo; if (!dlo) { if (!sp) { lwsl_err("%s: underflow\n", __func__); return; } if (lws_fx_comp(&st[sp].co.w, &st[sp - 1].co.w) > 0) st[sp - 1].co.w = st[sp].co.w; if (lws_fx_comp(&st[sp].co.h, &st[sp - 1].co.h) > 0) st[sp - 1].co.h = st[sp].co.h; // lwsl_notice("sp %d: passing back w: %d, h: %d\n", sp, st[sp - 1].co.w.whole, st[sp - 1].co.h.whole); sp--; continue; } lws_fx_add(&t1, &dlo->box.w, &dlo->box.x); // lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_LEFT]); lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_LEFT]); // lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_RIGHT]); // lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_RIGHT]); if (lws_fx_comp(&t1, &st[sp].co.w) > 0) st[sp].co.w = t1; lws_fx_add(&t1, &dlo->box.h, &dlo->box.y); // lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_TOP]); lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_TOP]); // lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_BOTTOM]); // lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_BOTTOM]); if (lws_fx_comp(&t1, &st[sp].co.h) > 0) st[sp].co.h = t1; d = dlo->list.next; if (d) st[sp].dlo = lws_container_of(d, lws_dlo_t, list); else st[sp].dlo = NULL; /* go into any children */ if (dlo->children.head) { if (++sp == LWS_ARRAY_SIZE(st)) { lwsl_err("%s: DLO stack overflow\n", __func__); return; } st[sp].dlo = lws_container_of( dlo->children.head, lws_dlo_t, list); st[sp].co.w.whole = 0; st[sp].co.h.whole = 0; st[sp].co.w.frac = 0; st[sp].co.h.frac = 0; } } dim->w = st[0].co.w; dim->h = st[0].co.h; if (parent->col_list.owner) { lhp_table_col_t *tc = lws_container_of(parent->col_list.owner, lhp_table_col_t, col_dlos); if (lws_fx_comp(&dim->w, &tc->width) < 0) { // lws_fx_add(&t1, &tc->width, &parent->padding[CCPAS_LEFT]); // lws_fx_add(&dim->w, &tc->width, &parent->padding[CCPAS_RIGHT]); dim->w = tc->width; } } if (parent->row_list.owner) { lhp_table_row_t *tr = lws_container_of(parent->row_list.owner, lhp_table_row_t, row_dlos); if (lws_fx_comp(&dim->h, &tr->height) < 0) { // lws_fx_add(&t1, &tr->height, &parent->padding[CCPAS_TOP]); lws_fx_add(&dim->h, &tr->height, &parent->padding[CCPAS_BOTTOM]); // dim->h = tr->height; } } /* lwsl_user("%s: dlo %p: FINAL w:%d -> %d h:%d -> %d\n", __func__, parent, parent->box.w.whole, dim->w.whole, parent->box.h.whole, dim->h.whole); */ } /* * Some DLO is changing height, adjust its height, and that of everybody below. */ void lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim) { lws_dlo_dim_t delta; if (!dim->w.whole && !dim->h.whole) return; /* adjust the target's width / height */ lws_fx_sub(&delta.w, &dim->w, &dlo->box.w); lws_fx_sub(&delta.h, &dim->h, &dlo->box.h); dlo->box.w = dim->w; dlo->box.h = dim->h; // lwsl_notice("%s: dlo %p: delta w:%d h:%d\n", __func__, dlo, delta.w.whole, delta.h.whole); /* move peers below him accordingly */ do { lws_dlo_t *dp = lws_container_of(dlo->list.owner, lws_dlo_t, children); if (!dlo->list.owner) break; /* * Adjust y pos of siblings below us */ do { dlo = lws_container_of(dlo->list.next, lws_dlo_t, list); if (dlo) { //lwsl_notice("%s: dlo %p: adj y %d -> %d\n", __func__, dlo, dlo->box.y.whole, dlo->box.y.whole + delta.h.whole); lws_fx_add(&dlo->box.y, &dlo->box.y, &delta.h); } } while (dlo); /* go up parent chain until toplevel adjusting height of * parent siblings below parent */ if (dp->flag_toplevel) break; dlo = dp; //lwsl_notice("%s: dlo %p: adj h by %d\n", __func__, dlo, delta.h.whole); lws_fx_add(&dlo->box.h, &dlo->box.h, &delta.h); } while (1); } //#if defined(_DEBUG) void lws_display_dl_dump(lws_displaylist_t *dl) { lws_display_render_stack_t st[12]; /* DLO child stack */ int sp = 0; lws_dll2_t *d = lws_dll2_get_head(&dl->dl); #if (_LWS_ENABLED_LOGS & dlodump_loglevel) static const char * const ind = " "; #endif char b[4][22], b1[4][22], dt[96]; if (!d) { lwsl_notice("%s: empty dl\n", __func__); return; } lwsl_notice("%s\n", __func__); memset(&st, 0, sizeof(st)); st[0].dlo = lws_container_of(d, lws_dlo_t, list); while (sp || st[0].dlo) { lws_dlo_t *dlo = st[sp].dlo; lws_box_t co; //lws_fx_t t2; if (!dlo) { if (!sp) { lwsl_err("%s: underflow\n", __func__); return; } sp--; continue; } lws_fx_add(&co.x, &st[sp].co.x, &dlo->box.x); lws_fx_add(&co.y, &st[sp].co.y, &dlo->box.y); co.w = dlo->box.w; co.h = dlo->box.h; lws_snprintf(dt, sizeof(dt), "rect: RGBA 0x%08X", (unsigned int)dlo->dc); if (dlo->_destroy == lws_display_dlo_text_destroy) { lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo); lws_snprintf(dt, sizeof(dt), "text: RGBA 0x%08X, chars: %u, %.*s", (unsigned int)dlo->dc, (unsigned int)text->text_len, (int)text->text_len, text->text ? text->text : "(empty)"); } #if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_UPNG) && defined(LWS_WITH_CLIENT) else if (dlo->_destroy == lws_display_dlo_png_destroy) lws_snprintf(dt, sizeof(dt), "png"); #endif #if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_JPEG) && defined(LWS_WITH_CLIENT) else if (dlo->_destroy == lws_display_dlo_jpeg_destroy) lws_snprintf(dt, sizeof(dt), "jpeg"); #endif lws_fx_string(&dlo->box.x, b[0], sizeof(b[0])); lws_fx_string(&dlo->box.y, b[1], sizeof(b[1])); lws_fx_string(&dlo->box.w, b[2], sizeof(b[2])); lws_fx_string(&dlo->box.h, b[3], sizeof(b[3])); lws_fx_string(&co.x, b1[0], sizeof(b1[0])); lws_fx_string(&co.y, b1[1], sizeof(b1[1])); lws_fx_string(&co.w, b1[2], sizeof(b1[2])); lws_fx_string(&co.h, b1[3], sizeof(b1[3])); lwsl_dlodump("%.*s %p box: (%s, %s) [%s x %s], co: (%s, %s) [%s x %s], %s\n", sp, ind, dlo, b[0], b[1], b[2], b[3], b1[0], b1[1], b1[2], b1[3], dt); d = dlo->list.next; if (d) st[sp].dlo = lws_container_of(d, lws_dlo_t, list); else st[sp].dlo = NULL; /* go into any children */ if (dlo->children.head) { if (sp + 1 == LWS_ARRAY_SIZE(st)) { lwsl_err("%s: DLO stack overflow\n", __func__); return; } st[++sp].dlo = lws_container_of( dlo->children.head, lws_dlo_t, list); st[sp].co = co; } } } //#endif /* * Go through every DLO once, setting its id->box to the final layout for the * related dlo, if any */ lws_stateful_ret_t lws_display_get_ids_boxes(lws_display_render_state_t *rs) { lws_dll2_t *d; rs->lowest_id_y = 0; d = lws_dll2_get_head(&rs->displaylist.dl); if (!d) /* nothing in dlo */ return LWS_SRET_OK; memset(&rs->st[0].co, 0, sizeof(rs->st[0].co)); rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list); while (rs->sp || rs->st[0].dlo) { lws_dlo_t *dlo = rs->st[rs->sp].dlo; lws_box_t co; lws_fx_t t2; if (!dlo) { rs->sp--; continue; } lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x); lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y); co.w = dlo->box.w; co.h = dlo->box.h; lws_fx_add(&t2, &co.y, &dlo->box.h); if (dlo->id) { lws_display_id_t *id = dlo->id; lwsl_debug("%s: set id box %s\n", __func__, id->id); id->box = co; dlo->id = NULL; /* decouple us */ } if (co.y.whole + co.h.whole > rs->lowest_id_y) { rs->lowest_id_y = (lws_display_scalar)(co.y.whole + co.h.whole); if (rs->lowest_id_y > rs->ic->wh_px[1].whole) rs->lowest_id_y = (lws_display_scalar)rs->ic->wh_px[1].whole; } /* next sibling at this level if any */ d = dlo->list.next; if (d) rs->st[rs->sp].dlo = lws_container_of(d, lws_dlo_t, list); else rs->st[rs->sp].dlo = NULL; /* go into any children */ if (dlo->children.head) { if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) { lwsl_err("%s: DLO stack overflow\n", __func__); return LWS_SRET_FATAL; } rs->st[++rs->sp].dlo = lws_container_of( dlo->children.head, lws_dlo_t, list); rs->st[rs->sp].co = co; continue; } } lws_display_render_dump_ids(&rs->ids); return LWS_SRET_OK; } lws_stateful_ret_t lws_display_list_render_line(lws_display_render_state_t *rs) { lws_dll2_t *d; if (rs->html == 1) return LWS_SRET_WANT_INPUT; if (!rs->sp && !rs->st[0].dlo) { /* starting a line */ d = lws_dll2_get_head(&rs->displaylist.dl); if (!d) /* nothing in dlo */ return LWS_SRET_OK; // memset(rs->line, 0, (size_t)rs->ic->wh_px[0].whole * // (rs->ic->greyscale ? 1 : 3)); memset(&rs->st[0].co, 0, sizeof(rs->st[0].co)); rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list); } while (rs->sp || rs->st[0].dlo) { lws_dlo_t *dlo = rs->st[rs->sp].dlo; lws_stateful_ret_t r; lws_box_t co; lws_fx_t t2; if (!dlo) { rs->sp--; continue; } // lwsl_notice("%s: curr %d: %d %d %d %d\n", __func__, (int)rs->curr, (int)dlo->box.x.whole, (int)dlo->box.y.whole, (int)dlo->box.w.whole, (int)dlo->box.h.whole); lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x); lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y); co.w = dlo->box.w; co.h = dlo->box.h; lws_fx_add(&t2, &co.y, &dlo->box.h); if (rs->curr > lws_fx_roundup(&t2)) { d = dlo->list.next; rs->st[rs->sp].dlo = d ? lws_container_of(d, lws_dlo_t, list) : NULL; lws_display_dlo_destroy(&dlo); continue; } #if 0 if (dlo->_destroy == lws_display_dlo_png_destroy) lwsl_err("png line %d %d %d %d\n", rs->curr, co.y.whole - 1, rs->st[rs->sp].co.y.whole, dlo->box.y.whole); #endif if (rs->curr >= co.y.whole - 1) { r = dlo->render(rs); //rs->ic, dlo, &rs->st[rs->sp].co, // rs->curr, rs->line, &dlo->nle[0]); if (r) return r; /* next sibling at this level if any */ d = dlo->list.next; if (d) rs->st[rs->sp].dlo = lws_container_of(d, lws_dlo_t, list); else rs->st[rs->sp].dlo = NULL; /* go into any children */ if (dlo->children.head) { if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) { lwsl_err("%s: DLO stack overflow\n", __func__); return LWS_SRET_FATAL; } rs->st[++rs->sp].dlo = lws_container_of( dlo->children.head, lws_dlo_t, list); rs->st[rs->sp].co = co; continue; } } else { /* next sibling at this level if any */ d = dlo->list.next; if (d) rs->st[rs->sp].dlo = lws_container_of(d, lws_dlo_t, list); else rs->st[rs->sp].dlo = NULL; } } return LWS_SRET_OK; } static int dlo_clean_table_rows(lws_dll2_t *d, void *user) { lhp_table_row_t *r = lws_container_of(d, lhp_table_row_t, list); lws_dll2_remove(d); lws_free(r); return 0; } static int dlo_clean_table_cols(lws_dll2_t *d, void *user) { lhp_table_col_t *c = lws_container_of(d, lhp_table_col_t, list); lws_dll2_remove(d); lws_free(c); return 0; } void lws_display_dlo_destroy(lws_dlo_t **r) { if (!(*r)) return; lws_dll2_remove(&(*r)->list); lws_dll2_remove(&(*r)->col_list); lws_dll2_remove(&(*r)->row_list); while ((*r)->children.head) { lws_dlo_t *d = lws_container_of((*r)->children.head, lws_dlo_t, list); lws_display_dlo_destroy(&d); } lws_dll2_foreach_safe(&(*r)->table_cols, NULL, dlo_clean_table_cols); lws_dll2_foreach_safe(&(*r)->table_rows, NULL, dlo_clean_table_rows); if ((*r)->_destroy) (*r)->_destroy(*r); lws_free_set_NULL(*r); *r = NULL; } void lws_display_list_destroy(lws_displaylist_t *dl) { if (!dl) return; while (dl->dl.head) { lws_dlo_t *d = lws_container_of(dl->dl.head, lws_dlo_t, list); lws_display_dlo_destroy(&d); } } lws_dlo_filesystem_t * lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f) { const lws_dlo_filesystem_t *b; lws_dlo_filesystem_t *a; b = lws_dlo_file_choose(cx, f->name); if (b) { lwsl_err("%s: dlo file %s already exists %p\n", __func__, b->name, b); lws_dlo_file_unregister((lws_dlo_filesystem_t **)&b); } a = lws_malloc(sizeof(*a), __func__); if (!a) return NULL; *a = *f; lws_dll2_clear(&a->list); lws_dll2_add_tail(&a->list, &cx->dlo_file); lwsl_err("%s: dlo file %s registered at %p\n", __func__, a->name, a); return a; } /* * Only needed with heap-alloc'd lws_dlo_filesystem_t */ void lws_dlo_file_unregister(lws_dlo_filesystem_t **f) { if (!*f) return; lws_dll2_remove(&(*f)->list); lws_free_set_NULL(*f); } void lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name) { lws_dlo_filesystem_t *a; a = (lws_dlo_filesystem_t *)lws_dlo_file_choose(cx, name); if (!a) return; lws_dll2_remove(&a->list); lws_free_set_NULL(a); } static int _lws_dlo_file_destroy(struct lws_dll2 *d, void *user) { lws_free(d); return 0; } void lws_dlo_file_destroy(struct lws_context *cx) { lws_dll2_foreach_safe(&cx->dlo_file, NULL, _lws_dlo_file_destroy); } const lws_dlo_filesystem_t * lws_dlo_file_choose(struct lws_context *cx, const char *name) { lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(&cx->dlo_file)) { const lws_dlo_filesystem_t *pn = lws_container_of(p, lws_dlo_filesystem_t, list); if (!strcmp(name, pn->name)) return pn; } lws_end_foreach_dll(p); return NULL; } static int lws_display_id_destroy(struct lws_dll2 *d, void *user) { lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); lws_dll2_remove(&id->list); lws_free(id); return 0; } void lws_display_render_free_ids(lws_display_render_state_t *rs) { lws_dll2_foreach_safe(&rs->ids, NULL, lws_display_id_destroy); } lws_display_id_t * lws_display_render_get_id(lws_display_render_state_t *rs, const char *_id) { lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&rs->ids)) { lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); if (!strcmp(_id, id->id)) return id; } lws_end_foreach_dll(d); return NULL; } lws_display_id_t * lws_display_render_add_id(lws_display_render_state_t *rs, const char *_id, void *priv) { lws_display_id_t *id; id = lws_display_render_get_id(rs, _id); if (id) { id->priv_user = priv; return id; } id = lws_zalloc(sizeof(*id), __func__); if (id) { lws_strncpy(id->id, _id, sizeof(id->id)); id->priv_user = priv; lws_dll2_add_tail(&id->list, &rs->ids); } return id; } void lws_display_render_dump_ids(lws_dll2_owner_t *ids) { lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ids)) { lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); if (!id->exists) lwsl_notice(" id: '%s' (not present)\n", id->id); else lwsl_notice(" id: '%s', (%d,%d), %dx%d\n", id->id, (int)id->box.x.whole, (int)id->box.y.whole, (int)id->box.w.whole, (int)id->box.h.whole); } lws_end_foreach_dll(d); } #if defined (LWS_WITH_FILE_OPS) int dlo_filesystem_fops_close(lws_fop_fd_t *fop_fd) { lws_free_set_NULL(*fop_fd); return 0; } lws_fileofs_t dlo_filesystem_fops_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t pos) { if (pos < 0) fop_fd->pos = 0; else if (pos >= (long long)fop_fd->len) fop_fd->pos = fop_fd->len; else fop_fd->pos = (lws_filepos_t)pos; return (lws_fileofs_t)fop_fd->pos; } int dlo_filesystem_fops_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, uint8_t *buf, lws_filepos_t len) { *amount = 0; return -1; } int dlo_filesystem_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, uint8_t *buf, lws_filepos_t len) { const uint8_t *p = (uint8_t *)fop_fd->filesystem_priv; lws_filepos_t amt = *amount; *amount = 0; if (fop_fd->len <= fop_fd->pos) return 0; if (amt > fop_fd->len - fop_fd->pos) amt = fop_fd->len - fop_fd->pos; if (amt > len) amt = len; memcpy(buf, p + fop_fd->pos, (size_t)amt); fop_fd->pos += amt; *amount = amt; return 0; } lws_fop_fd_t lws_dlo_filesystem_fops_open(const struct lws_plat_file_ops *fops_own, const struct lws_plat_file_ops *fops, const char *vfs_path, const char *vpath, lws_fop_flags_t *flags) { const lws_dlo_filesystem_t *f = NULL; lws_fop_fd_t fop_fd; // lwsl_err("%s: %s\n", __func__, vpath); f = lws_dlo_file_choose(fops->cx, vpath); if (f) { /* we will handle it then */ fop_fd = lws_zalloc(sizeof(*fop_fd), __func__); if (!fop_fd) return NULL; fop_fd->fops = fops_own; fop_fd->filesystem_priv = (void *)f->data; fop_fd->pos = 0; fop_fd->len = f->len; // lwsl_notice("%s: Opened %s\n", __func__, vpath); return fop_fd; } else lwsl_err("%s: failed to open %s\n", __func__, vpath); return NULL; } const struct lws_plat_file_ops lws_dlo_fops = { .LWS_FOP_OPEN = lws_dlo_filesystem_fops_open, .LWS_FOP_CLOSE = dlo_filesystem_fops_close, .LWS_FOP_SEEK_CUR = dlo_filesystem_fops_seek_cur, .LWS_FOP_READ = dlo_filesystem_fops_read, .LWS_FOP_WRITE = dlo_filesystem_fops_write, .fi = { { "dlofs/", 6 } }, }; #endif