First commit with working display
This commit is contained in:
2
managed_components/lvgl__lvgl/scripts/.gitignore
vendored
Normal file
2
managed_components/lvgl__lvgl/scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
CHANGELOG_LAST.md
|
||||
CHANGELOG_LAST.rst
|
||||
1547
managed_components/lvgl__lvgl/scripts/LVGLImage.py
Normal file
1547
managed_components/lvgl__lvgl/scripts/LVGLImage.py
Normal file
File diff suppressed because it is too large
Load Diff
59
managed_components/lvgl__lvgl/scripts/build_html_examples.sh
Normal file
59
managed_components/lvgl__lvgl/scripts/build_html_examples.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# These variables allow us to specify an alternate repository URL and commit reference
|
||||
# This is particularly useful when running in CI environments for pull requests
|
||||
# where we need to build from the contributor's forked repository
|
||||
ARG_1="${1:-}"
|
||||
ARG_2="${2:-}"
|
||||
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
rm -rf emscripten_builder
|
||||
git clone https://github.com/lvgl/lv_sim_emscripten.git emscripten_builder
|
||||
cd emscripten_builder
|
||||
if [ "$ARG_1" != "--symlink" ]; then
|
||||
REPO_URL="$ARG_1"
|
||||
COMMIT_REF="$ARG_2"
|
||||
git submodule update --init -- lvgl
|
||||
if [ -n "$REPO_URL" ] && [ -n "$COMMIT_REF" ]; then
|
||||
cd lvgl
|
||||
echo "Using provided repo URL: $REPO_URL and commit ref: $COMMIT_REF for lvgl submodule"
|
||||
git remote set-url origin "$REPO_URL"
|
||||
git fetch origin
|
||||
git checkout "$COMMIT_REF"
|
||||
cd ..
|
||||
fi
|
||||
else
|
||||
SYMLINK_TARGET="$ARG_2"
|
||||
echo "Using provided symbolic link to LVGL directory (should be absolute): $SYMLINK_TARGET"
|
||||
rmdir lvgl # remove the uninitialized submodule empty dir
|
||||
ln -s -T "$SYMLINK_TARGET" lvgl
|
||||
fi
|
||||
|
||||
# Grab the path to emscripten's port examplelist.c before changing to LVGL's directory
|
||||
EXAMPLE_LIST_C=$(pwd)/examplelist.c
|
||||
cd lvgl
|
||||
scripts/genexamplelist.sh > $EXAMPLE_LIST_C
|
||||
cd ..
|
||||
|
||||
# Generate lv_conf
|
||||
LV_CONF_PATH=`pwd`/lvgl/configs/ci/docs/lv_conf_docs.h
|
||||
|
||||
python ./lvgl/scripts/generate_lv_conf.py \
|
||||
--template lvgl/lv_conf_template.h \
|
||||
--config $LV_CONF_PATH \
|
||||
--defaults lvgl/configs/ci/docs/lv_conf_docs.defaults
|
||||
|
||||
mkdir cmbuild
|
||||
cd cmbuild
|
||||
emcmake cmake .. -DLV_BUILD_CONF_PATH=$LV_CONF_PATH -DLVGL_CHOSEN_DEMO=lv_example_noop -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
emmake make -j$(nproc)
|
||||
rm -rf CMakeFiles
|
||||
cd ..
|
||||
if [ "$ARG_1" != "--symlink" ]; then
|
||||
cp -a cmbuild ../docs/src/_static/built_lv_examples
|
||||
else
|
||||
cp -a cmbuild lvgl/docs/src/_static/built_lv_examples
|
||||
fi
|
||||
cd ..
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from argparse import RawTextHelpFormatter
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description="""Create fonts for LVGL including the built-in symbols. lv_font_conv needs to be installed. See https://github.com/lvgl/lv_font_conv
|
||||
Example: python built_in_font_gen.py --size 16 -o lv_font_roboto_16.c --bpp 4 -r 0x20-0x7F""", formatter_class=RawTextHelpFormatter)
|
||||
parser.add_argument('-s', '--size',
|
||||
type=int,
|
||||
metavar = 'px',
|
||||
nargs='?',
|
||||
help='Size of the font in px')
|
||||
parser.add_argument('--bpp',
|
||||
type=int,
|
||||
metavar = '1,2,4',
|
||||
nargs='?',
|
||||
help='Bit per pixel')
|
||||
parser.add_argument('-r', '--range',
|
||||
nargs='+',
|
||||
metavar = 'start-end',
|
||||
default=['0x20-0x7F,0xB0,0x2022'],
|
||||
help='Ranges and/or characters to include. Default is 0x20-7F (ASCII). E.g. -r 0x20-0x7F, 0x200, 324')
|
||||
parser.add_argument('--symbols',
|
||||
nargs='+',
|
||||
metavar = 'sym',
|
||||
default=[''],
|
||||
help=u'Symbols to include. E.g. -s ÁÉŐ'.encode('utf-8'))
|
||||
parser.add_argument('--font',
|
||||
metavar = 'file',
|
||||
nargs='?',
|
||||
default='Montserrat-Medium.ttf',
|
||||
help='A TTF or WOFF file')
|
||||
parser.add_argument('-o', '--output',
|
||||
nargs='?',
|
||||
metavar='file',
|
||||
help='Output file name. E.g. my_font_20.c')
|
||||
parser.add_argument('--compressed', action='store_true',
|
||||
help='Compress the bitmaps')
|
||||
parser.add_argument('--subpx', action='store_true',
|
||||
help='3 times wider letters for sub pixel rendering')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.compressed == False:
|
||||
compr = "--no-compress --no-prefilter"
|
||||
else:
|
||||
compr = ""
|
||||
|
||||
if len(args.symbols[0]) != 0:
|
||||
args.symbols[0] = "--symbols " + args.symbols[0]
|
||||
|
||||
subpx = ""
|
||||
if args.subpx: subpx = "--lcd"
|
||||
|
||||
#Built in symbols
|
||||
syms = "61441,61448,61451,61452,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650"
|
||||
|
||||
#Run the command (Add degree and bullet symbol)
|
||||
cmd = ["lv_font_conv"]
|
||||
if subpx:
|
||||
cmd.append(subpx)
|
||||
if compr:
|
||||
cmd.extend(compr.split())
|
||||
cmd.extend(["--bpp", str(args.bpp), "--size", str(args.size), "--font", args.font, "-r", args.range[0]])
|
||||
if args.symbols[0]:
|
||||
cmd.extend(args.symbols[0].split())
|
||||
cmd.extend(["--font", "FontAwesome5-Solid+Brands+Regular.woff", "-r", syms, "--format", "lvgl"])
|
||||
if args.output:
|
||||
cmd.extend(["-o", args.output])
|
||||
cmd.append("--force-fast-kern-format")
|
||||
subprocess.run(cmd, check=True)
|
||||
@@ -0,0 +1,99 @@
|
||||
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
||||
|
||||
Bitstream Vera Fonts Copyright
|
||||
------------------------------
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
||||
a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute the
|
||||
Font Software, including without limitation the rights to use, copy, merge,
|
||||
publish, distribute, and/or sell copies of the Font Software, and to permit
|
||||
persons to whom the Font Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice shall
|
||||
be included in all copies of one or more of the Font Software typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in particular
|
||||
the designs of glyphs or characters in the Fonts may be modified and
|
||||
additional glyphs or characters may be added to the Fonts, only if the fonts
|
||||
are renamed to names not containing either the words "Bitstream" or the word
|
||||
"Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts or Font
|
||||
Software that has been modified and is distributed under the "Bitstream
|
||||
Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but no
|
||||
copy of one or more of the Font Software typefaces may be sold by itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
||||
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
||||
FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font Software
|
||||
without prior written authorization from the Gnome Foundation or Bitstream
|
||||
Inc., respectively. For further information, contact: fonts at gnome dot
|
||||
org.
|
||||
|
||||
Arev Fonts Copyright
|
||||
------------------------------
|
||||
|
||||
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and
|
||||
associated documentation files (the "Font Software"), to reproduce
|
||||
and distribute the modifications to the Bitstream Vera Font Software,
|
||||
including without limitation the rights to use, copy, merge, publish,
|
||||
distribute, and/or sell copies of the Font Software, and to permit
|
||||
persons to whom the Font Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Tavmjong Bah" or the word "Arev".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Tavmjong Bah Arev" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Tavmjong Bah shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other
|
||||
dealings in this Font Software without prior written authorization
|
||||
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
||||
. fr.
|
||||
|
||||
$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
|
||||
@@ -0,0 +1,165 @@
|
||||
Fonticons, Inc. (https://fontawesome.com)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Font Awesome Free License
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
|
||||
The Font Awesome Free download is licensed under a Creative Commons
|
||||
Attribution 4.0 International License and applies to all icons packaged
|
||||
as SVG and JS file types.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Fonts: SIL OFL 1.1 License
|
||||
|
||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
Copyright (c) 2024 Fonticons, Inc. (https://fontawesome.com)
|
||||
with Reserved Font Name: "Font Awesome".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
SIL OPEN FONT LICENSE
|
||||
Version 1.1 - 26 February 2007
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting — in part or in whole — any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
Copyright 2024 Fonticons, Inc.
|
||||
|
||||
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.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Attribution
|
||||
|
||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Brand Icons
|
||||
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
@@ -0,0 +1,165 @@
|
||||
Fonticons, Inc. (https://fontawesome.com)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Font Awesome Free License
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
|
||||
The Font Awesome Free download is licensed under a Creative Commons
|
||||
Attribution 4.0 International License and applies to all icons packaged
|
||||
as SVG and JS file types.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Fonts: SIL OFL 1.1 License
|
||||
|
||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
Copyright (c) 2022 Fonticons, Inc. (https://fontawesome.com)
|
||||
with Reserved Font Name: "Font Awesome".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
SIL OPEN FONT LICENSE
|
||||
Version 1.1 - 26 February 2007
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting — in part or in whole — any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
Copyright 2022 Fonticons, Inc.
|
||||
|
||||
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.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Attribution
|
||||
|
||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Brand Icons
|
||||
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
@@ -0,0 +1,94 @@
|
||||
Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,94 @@
|
||||
Copyright 2021 Google Inc. All Rights Reserved.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font
|
||||
Name 'Source'. Source is a trademark of Adobe in the United States
|
||||
and/or other countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License,
|
||||
Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font
|
||||
creation efforts of academic and linguistic communities, and to
|
||||
provide a free and open framework in which fonts may be shared and
|
||||
improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply to
|
||||
any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software
|
||||
components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to,
|
||||
deleting, or substituting -- in part or in whole -- any of the
|
||||
components of the Original Version, by changing formats or by porting
|
||||
the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed,
|
||||
modify, redistribute, and sell modified and unmodified copies of the
|
||||
Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in
|
||||
Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the
|
||||
corresponding Copyright Holder. This restriction only applies to the
|
||||
primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created using
|
||||
the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,97 @@
|
||||
-------------------------------
|
||||
UBUNTU FONT LICENCE Version 1.0
|
||||
-------------------------------
|
||||
|
||||
PREAMBLE
|
||||
This licence allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely. The fonts, including any derivative works, can be
|
||||
bundled, embedded, and redistributed provided the terms of this licence
|
||||
are met. The fonts and derivatives, however, cannot be released under
|
||||
any other licence. The requirement for fonts to remain under this
|
||||
licence does not require any document created using the fonts or their
|
||||
derivatives to be published under this licence, as long as the primary
|
||||
purpose of the document is not to be a vehicle for the distribution of
|
||||
the fonts.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this licence and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Original Version" refers to the collection of Font Software components
|
||||
as received under this licence.
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to
|
||||
a new environment.
|
||||
|
||||
"Copyright Holder(s)" refers to all individuals and companies who have a
|
||||
copyright ownership of the Font Software.
|
||||
|
||||
"Substantially Changed" refers to Modified Versions which can be easily
|
||||
identified as dissimilar to the Font Software by users of the Font
|
||||
Software comparing the Original Version with the Modified Version.
|
||||
|
||||
To "Propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification and with or without charging
|
||||
a redistribution fee), making available to the public, and in some
|
||||
countries other activities as well.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
This licence does not grant any rights under trademark law and all such
|
||||
rights are reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of the Font Software, to propagate the Font Software, subject to
|
||||
the below conditions:
|
||||
|
||||
1) Each copy of the Font Software must contain the above copyright
|
||||
notice and this licence. These can be included either as stand-alone
|
||||
text files, human-readable headers or in the appropriate machine-
|
||||
readable metadata fields within text or binary files as long as those
|
||||
fields can be easily viewed by the user.
|
||||
|
||||
2) The font name complies with the following:
|
||||
(a) The Original Version must retain its name, unmodified.
|
||||
(b) Modified Versions which are Substantially Changed must be renamed to
|
||||
avoid use of the name of the Original Version or similar names entirely.
|
||||
(c) Modified Versions which are not Substantially Changed must be
|
||||
renamed to both (i) retain the name of the Original Version and (ii) add
|
||||
additional naming elements to distinguish the Modified Version from the
|
||||
Original Version. The name of such Modified Versions must be the name of
|
||||
the Original Version, with "derivative X" where X represents the name of
|
||||
the new work, appended to that name.
|
||||
|
||||
3) The name(s) of the Copyright Holder(s) and any contributor to the
|
||||
Font Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except (i) as required by this licence, (ii) to
|
||||
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
|
||||
their explicit written permission.
|
||||
|
||||
4) The Font Software, modified or unmodified, in part or in whole, must
|
||||
be distributed entirely under this licence, and must not be distributed
|
||||
under any other licence. The requirement for fonts to remain under this
|
||||
licence does not affect any document created using the Font Software,
|
||||
except any version of the Font Software extracted from a document
|
||||
created using the Font Software may only be distributed under this
|
||||
licence.
|
||||
|
||||
TERMINATION
|
||||
This licence becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
||||
DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
<html>
|
||||
<meta charset="UTF-8" />
|
||||
<head>
|
||||
<title>Unscii - a bitmapped Unicode font for blocky graphics</title>
|
||||
<style>
|
||||
<!--
|
||||
@font-face{font-family:"unscii16";
|
||||
src: url("unscii-16.woff") format("woff"),
|
||||
url("unscii-16.ttf") format("ttf"); }
|
||||
@font-face{font-family:"unscii8";
|
||||
src: url("unscii-8.woff") format("woff"),
|
||||
url("unscii-8.ttf") format("ttf"); }
|
||||
a{color:#003;}
|
||||
ul{list-style-type:square;}
|
||||
h1{font-family:"unscii16",monospace;font-size:32px;text-align:center;}
|
||||
body{font-family:"unscii16",monospace; background-color:#ccc;color:#000;width:640px;}
|
||||
pre{font-family:"unscii16",monospace; font-size:16px;line-height:16px}
|
||||
-->
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<p align="center"><img src="unsciilogo.png" alt="UNSCII" /></p>
|
||||
|
||||
<p>Unscii is a set of bitmapped Unicode fonts based on classic system fonts.
|
||||
Unscii attempts to support character cell art well while also being suitable
|
||||
for terminal and programming use.</p>
|
||||
|
||||
<p>The two main variants are unscii-8 (8×8 pixels per glyph) and unscii-16
|
||||
(8×16). There are also several alternative styles for unscii-8, as well as
|
||||
an 8x16 "full" variant that incorporates missing Unicode glyphs from
|
||||
Fixedsys Excelsior and GNU Unifont. "unscii-16-full" falls under GPL because
|
||||
of how Unifont is licensed; the other variants are in the Public Domain.</p>
|
||||
|
||||
<p>Unscii was created by Viznut.</p>
|
||||
|
||||
<br />
|
||||
|
||||
<h1>UNSCII 2.0</h1>
|
||||
|
||||
<p>In 2020-03-10, the new <a
|
||||
href="http://www.unicode.org/versions/Unicode13.0.0/">Unicode version
|
||||
13.0</a> added 214 graphics characters for "legacy computing" (including,
|
||||
among all, the missing PETSCII characters, and a majority of missing
|
||||
Teletext/Videotex characters). Most of these were already included in Unscii
|
||||
1.x, but now I have been able to give them proper Unicode mappings as well.
|
||||
This is the main reason for the Unscii 2.0 release.</p>
|
||||
|
||||
<p>Additionally, Unscii 2.0 fixes errors in some characters, legibility in
|
||||
some others and adds a bunch of new ones.</p>
|
||||
|
||||
<p>A test picture representing what is currently available in Unicode (feel
|
||||
free to copy-paste it to your editor to see what it looks like in other
|
||||
fonts):</p>
|
||||
|
||||
<pre>
|
||||
╎┆┊ ╱🭽▔🭾╲ 🮲🮳 🮸🮀🮵🮶🮀🮁🮁🮀🮼🯁🯂🯃 ▵ ↑ ◬
|
||||
╶─╴╺━╸ ═ ╎┆┊ ⎹ ⎸▣⎹ ⎸ ▝▛▀▜▘ 🯲🯷🯶 △ ▴ ╽ ◭⬘◮
|
||||
╷┌┬┐┍┯┑╒╤╕╏┇┋ 🮷 🭼▁🭿 ⎸ ▚▌█▐▞ 🯹🯵🯱 🯰 ▁▂▃▄▅▆▇█ ◃◅◁╳▷▻▹ ▲ ←╼╋╾→ ◩⬒⬔
|
||||
│├┼┤┝┿┥╞╪╡╏┇┋ ⎹╱ ╳ ╲⎸ ▗▙▄▟▖ 🯴🯳🯸 █🮆🮅🮄▀🮃🮂▔⎹ ▽ ◂◄◀🮽▶►▸╿ ⮝ ⬖◧◫◨⬗
|
||||
╵└┴┘┕┷┙╘╧╛┞╀┦ ▔▔▔▔▔ 🬑🬜🬰🬪🬟 🮞🮟 ▕▉ ◞◡◯◡ ◎🭵 ▿ ▼ ↓ ⮜◈⮞ ⬕⬓◪
|
||||
╻┎┰┒┏┳┓ ┭╆╈╅┮╍╍╌╌ 🬥🬦🬍🬲🬵🬹🬱🬷🬌🬓🬙 🮝🮜 🮇▊◝◠◯◉◯◡◟🭴 ▾ ⮟ ◕ ⬙ ◔
|
||||
┃┠╂┨┣╋┫ ╺┽╊◙╉┾┅┅┄┄ 🬔🬡🬖🬻🬞🬭🬏🬺🬢🬒🬧 🮈▋◍ ◠◯◠◜ 🭳 ◿◺
|
||||
╹┖┸┚┗┻┛ ━┵╄╇╃┶┉┉┈┈ 🬃🬤🬫🬴🬠🬋🬐🬸🬛🬗🬇 🭇🬼 ▐▌ ◌🮣🮢 🮦 🭲 ◹◸ 🭯 🮀⚞⚟🮀 🯊 ◙◛◶─◵
|
||||
╓╥╖ ╔╦╗┢╁┪ ┟┱┲┧ 🬣🬯🬈🬬🬁🬂🬀🬝🬅🬮🬘 🭢🭗 🮉▍ 🮤🮪🮫🮥🮧 🭱 🭯 🭮◙🭬╭──╮⎫🮻⎧ ◘◙│◲┼◱╭◒╮
|
||||
║╟╫╢🮐🮒🮐╠╬╣ ╹┃ ┡┹┺┩ 🬳🬉🬩🬕🬊🬎🬆🬨🬚🬄🬶 🭊🬿 🮊▎ 🮩🮬🮭🮨 🭰 ◢🭫◣ 🮚 │ ▢ ⎮🏎⎪ ◙◚◷┼◴│◑╋◐
|
||||
╙╨╜🮔 ╚╩╝ 🯆 🯅 🯇 🮣🮢 🯉 🯈 🭥🭚 🮋▏🮮 🮡🮠 ⎸🭮🭪◆🭨🮛🮿🭬╰─🮯─╯⎬⎯⎨ ◳─◰╰◓╯
|
||||
░░🮐🮑🮐▓▓██🮗🮗▤▤▥▥▦▦▩▩▧▧🮘🮘🮙🮙▨▨🮕🮕🮖🮖 🭋🭀 █▁🭻🭺🭹🭸🭷🭶▔ ◥🭩◤ 🭭 ⎮⯊⎪ ▱▰ ▭▬
|
||||
░░▒🮎▒▓▓██🮗🮗▤▤▥▥▦▦▩▩▧▧🮘🮘🮙🮙▨▨🮕🮕🮖🮖 🭦🭛 🮰 🭇🬼🭭 🭊🬿 🭋🭀 ⎭⯋⎩ ▯▮ ▫◻□■◼▪⬝·
|
||||
🮌█🮍 ╲╱ 🭇🬼🭈🬽🭉🬾◢◣🭇🭃🭎🬼🭈🭆🭂🭍🭑🬽🭉🭁🭌🬾🭈🭄🭏🬽🭅🭐 ◦○◯⬤◖◗ ⬫⬦⬨♢◊◇◆♦⬧⬥⬩⬪
|
||||
▒🮏▒ 🭢🭗🭣🭘🭤🭙◥◤🭢🭔🭟🭗🭣🭧🭓🭞🭜🭘🭤🭒🭝🭙🭣🭕🭠🭘🭖🭡 ∘⭘●
|
||||
🭢🭗 🭥🭚 🭦🭛 •
|
||||
</pre>
|
||||
|
||||
<h1>EXAMPLES</h1>
|
||||
|
||||
<p>Here are some conversions of legacy character set art into Unscii.</p>
|
||||
|
||||
<p>Amiga ansi: Divine Stylers by Hellbeard, as rendered with unscii-16.
|
||||
<a href="http://sixteencolors.net/pack/impure54/xz-dvn.ans">Source</a></p>
|
||||
|
||||
<img src="xz-dvn.png" />
|
||||
|
||||
<p>PC ansi: Ansi Love by Rad Man, as rendered with unscii-16.
|
||||
<a
|
||||
href="http://sixteencolors.net/pack/blocktronics_blockalypse/rad-LOVE.ANS">Source</a></p>
|
||||
|
||||
<img src="rad-love.png" />
|
||||
|
||||
<p>Commodore 64 petscii pictures as rendered with unscii-8, using the
|
||||
256-color xterm palette: I Has Floppy by Redcrab; The First Ball by
|
||||
Dr.TerrorZ; Gary by Mermaid.</p>
|
||||
<p align="center">
|
||||
<img src="ihasfloppy.png" />
|
||||
<img src="thefirstball.png" />
|
||||
<img src="gary.png" />
|
||||
</p>
|
||||
|
||||
<p>The source code package includes a generic bitmap-to-unscii converter. Here's an
|
||||
example of a conversion to unscii-8 using the 256-color xterm
|
||||
palette, without dithering:</p>
|
||||
<p align="center">
|
||||
<img src="tta.png" />
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<h1>DOWNLOADS</h1>
|
||||
|
||||
<p>HEX and PCF are the only actual bitmapped formats here. HEX is the same
|
||||
simple hexdump format as used by the Unifont project. TTF, OTF and WOFF
|
||||
are vectorized.</p>
|
||||
|
||||
<p>NOTE: Due to format limitations, the PCF versions lack all the characters
|
||||
above U+FFFF! However, all the new graphics characters are provided in the
|
||||
good old PUA range as well. A mapping is in the file <a
|
||||
href="uns2uni.tr">uns2uni.tr</a>.</p>
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<img src="unscii16.png" alt="[Some Unscii16 glyphs]" />
|
||||
</td><td>
|
||||
<p>unscii-16: <a href="unscii-16.hex">hex</a> <a href="unscii-16.pcf">pcf</a> <a href="unscii-16.ttf">ttf</a> <a href="unscii-16.otf">otf</a> <a href="unscii-16.woff">woff</a><br />
|
||||
unscii-16-full: <a href="unscii-16-full.hex">hex</a> <a href="unscii-16-full.pcf">pcf</a> <a href="unscii-16-full.ttf">ttf</a> <a href="unscii-16-full.otf">otf</a> <a href="unscii-16-full.woff">woff</a><br />
|
||||
8x16. The latter is recommended for serious terminal use where a large
|
||||
Unicode coverage is needed. (Warning: unscii16-full files range from 2
|
||||
to 12 megabytes in size; the others range from 40 to 400 kilobytes.)</p>
|
||||
</td></tr>
|
||||
|
||||
<tr bgcolor="#999">
|
||||
<td>
|
||||
<img src="unscii8.png" alt="[Some Unscii8 glyphs]" />
|
||||
</td><td>
|
||||
<p>unscii-8: <a href="unscii-8.hex">hex</a> <a href="unscii-8.pcf">pcf</a> <a href="unscii-8.ttf">ttf</a> <a href="unscii-8.otf">otf</a> <a href="unscii-8.woff">woff</a></p>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<img src="unscii8-tall.png" alt="[Ascii range in Unscii8-tall" />
|
||||
</td><td>
|
||||
<p>unscii-8-tall: <a href="unscii-8-tall.hex">hex</a> <a href="unscii-8-tall.pcf">pcf</a> <a href="unscii-8-tall.ttf">ttf</a> <a href="unscii-8-tall.otf">otf</a> <a href="unscii-8-tall.woff">woff</a><br />
|
||||
Double-height version of unscii8.</p>
|
||||
</td></tr>
|
||||
|
||||
<tr bgcolor="#999">
|
||||
<td>
|
||||
<img src="unscii8-thin.png" alt="[Ascii range in Unscii8-tall" />
|
||||
</td><td>
|
||||
<p>unscii-8-thin: <a href="unscii-8-thin.hex">hex</a> <a href="unscii-8-thin.pcf">pcf</a> <a href="unscii-8-thin.ttf">ttf</a> <a href="unscii-8-thin.otf">otf</a> <a href="unscii-8-thin.woff">woff</a><br />
|
||||
Based on system fonts with 1-pixel-wide lines.</p>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<img src="unscii8-alt.png" alt="[Ascii range in Unscii8-alt" />
|
||||
</td><td>
|
||||
<p>unscii-8-alt: <a href="unscii-8-alt.hex">hex</a> <a href="unscii-8-alt.pcf">pcf</a> <a href="unscii-8-alt.ttf">ttf</a> <a href="unscii-8-alt.otf">otf</a> <a href="unscii-8-alt.woff">woff</a><br />
|
||||
Based on the more peculiar glyph forms of the reference fonts.</p>
|
||||
</td></tr>
|
||||
|
||||
<tr bgcolor="#999">
|
||||
<td>
|
||||
<img src="unscii8-mcr.png" alt="[Ascii range in Unscii8-alt" />
|
||||
</td><td>
|
||||
<p>unscii-8-mcr: <a href="unscii-8-mcr.hex">hex</a> <a href="unscii-8-mcr.pcf">pcf</a> <a href="unscii-8-mcr.ttf">ttf</a> <a href="unscii-8-mcr.otf">otf</a> <a href="unscii-8-mcr.woff">woff</a><br />
|
||||
Based on retrofuturistic MCR-like 8×8 fonts used in various games, demos,
|
||||
etc.</p>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<img src="unscii8-fantasy.png" alt="[Ascii range in Unscii8-alt" />
|
||||
</td><td>
|
||||
<p>unscii-8-fantasy: <a href="unscii-8-fantasy.hex">hex</a> <a href="unscii-8-fantasy.pcf">pcf</a> <a href="unscii-8-fantasy.ttf">ttf</a> <a href="unscii-8-fantasy.otf">otf</a> <a href="unscii-8-fantasy.woff">woff</a><br />
|
||||
Based on fonts used in fantasy games.</p>
|
||||
</td></tr>
|
||||
|
||||
<tr bgcolor="#999">
|
||||
<td>
|
||||
|
||||
</td><td>
|
||||
<p><a href="unscii-2.1-src.tar.gz">Source code for current Unscii version (2.1)</a></p>
|
||||
<p><a href="unscii-2.0-src.tar.gz">Source code for Unscii 2.0</a></p>
|
||||
<p><a href="unscii-1.1-src.tar.gz">Source code for Unscii 1.1</a></p>
|
||||
</td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<h1>BACKSTORY</h1>
|
||||
|
||||
<p>Years ago, I noticed that Unicode had a bunch of pseudographic characters
|
||||
that could be used to enrichen Ansi art. However, no one seemed to use them.
|
||||
Even MUDs that used the 256-color Xterm palette and had no issues with
|
||||
Unicode still preferred to stick to the blocks available in the MS-DOS
|
||||
codepage 437.</p>
|
||||
|
||||
<p>After looking into existing Unicode fonts, the reason became obvious: the
|
||||
implementation of non-CP437 graphics characters was shaky at best. Unicode
|
||||
Consortium doesn't even care how pseudographics are implemented. It was a
|
||||
kind of chicken-and-egg problem: No commonly accepted Unicode graphics font,
|
||||
no Unicode art scene; no art scene, no font support. The idea of an
|
||||
art-compatible Unicode font was born.</p>
|
||||
|
||||
<p>For Unscii, I studied a bunch of classic system fonts and how their
|
||||
characters had been used in Ascii and "extended-Ascii" art.</p>
|
||||
|
||||
<p>8×8 system fonts can be divided in two major categories according to
|
||||
their line thickness: 1-pixel and 2-pixel. 2-pixel-wide lines are used in
|
||||
more prominent classic systems, so I chose it. Also, 2-pixel 8×8 system
|
||||
fonts are surprisingly similar to one another which made it easier to choose
|
||||
neutral shapes.</p>
|
||||
|
||||
<p>The basic look of the 8×8 variant of Unscii is based on the following
|
||||
systems:</p>
|
||||
<ul>
|
||||
<li>Amiga (Topaz-8)</li>
|
||||
<li>Amstrad CPC</li>
|
||||
<li>Atari 8-bit (as in 800, XL etc.)</li>
|
||||
<li>Atari Arcade (the iconic ROM font)</li>
|
||||
<li>Atari 32-bit (as in ST etc.)</li>
|
||||
<li>BBC Micro (graphics mode font)</li>
|
||||
<li>Commodore 64</li>
|
||||
<li>IBM PC (the 8×8 ROM font as in CGA, or VGA 80×50)</li>
|
||||
</ul>
|
||||
|
||||
<p>The 8×16 variant of Unscii has been mostly derived from the 8×8 variant
|
||||
by using a set of transformation principles. When in doubt, the following
|
||||
fonts have been looked at for additional reference:</p>
|
||||
<ul>
|
||||
<li>Windows Fixedsys 8×15 (and its modern successor Fixedsys Excelsior)</li>
|
||||
<li>IBM PC VGA ROM font(s) (and their modern successor U_VGA)</li>
|
||||
<li>X Window System fonts 8x13(B) and 9x15(B)</li>
|
||||
<li>Classic Macintosh 12-point Monaco</li>
|
||||
<li>Digital VT420 10×16 font (used in the 80×24 mode)</li>
|
||||
<li>Modern monospaced vector fonts: DejaVu Sans Mono, Lucida Console,
|
||||
Inconsolata</li>
|
||||
</ul>
|
||||
|
||||
<p>In general, neutral shapes are preferred, unless art, legibility or
|
||||
readability require otherwise: The characters /\XY are connective because of
|
||||
their connetive use in ascii art, and the serifs in iIl are longer than in
|
||||
most classic systems.</p>
|
||||
|
||||
<p>Whenever a 8×16 shape has not been defined, Unscii falls back to
|
||||
height-doubled 8×8.</p>
|
||||
|
||||
<p>I also studied game fonts and thin-line system fonts. This resulted in
|
||||
the variants unscii-8-thin, unscii-8-mcr and unscii-8-fantasy.</p>
|
||||
|
||||
<p>When studying legacy character sets, I found literally hundreds of
|
||||
characters without proper Unicode codepoints. These are mapped in the PUA
|
||||
range as follows:</p>
|
||||
|
||||
<ul>
|
||||
<li>U+E080..E0FF: Teletext/Videotex block mosaics.</li>
|
||||
<li>U+E100..: The most prominent and useful non-Unicode pseudographics:
|
||||
everything found in PETSCII, Videotex smooth mosaics, extra shades,
|
||||
round corners, X/Y doublers.</li>
|
||||
<li>U+E800..: Somewhat stranger but still potentially useful: junctions with
|
||||
border-aligned lines, diagonal line junctions, non-straight lines, weirder
|
||||
fill patterns, etc.</li>
|
||||
<li>U+EC00..: Total oddities. Mostly game-oriented bitmaps and other
|
||||
depictive characters from Sharp MZ, Aquarius, etc.</li>
|
||||
</ul>
|
||||
|
||||
<p>Since Unicode 13.0, many of these are also available in Unicode, but
|
||||
the PUA mappings are retained for compatibility.</p>
|
||||
|
||||
<br />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
print("Generating 8 px")
|
||||
os.system("./built_in_font_gen.py --size 8 -o lv_font_montserrat_8.c --bpp 4")
|
||||
|
||||
print("\nGenerating 10 px")
|
||||
os.system("./built_in_font_gen.py --size 10 -o lv_font_montserrat_10.c --bpp 4")
|
||||
|
||||
print("\nGenerating 12 px")
|
||||
os.system("./built_in_font_gen.py --size 12 -o lv_font_montserrat_12.c --bpp 4")
|
||||
|
||||
print("\nGenerating 14 px")
|
||||
os.system("./built_in_font_gen.py --size 14 -o lv_font_montserrat_14.c --bpp 4")
|
||||
|
||||
print("\nGenerating 16 px")
|
||||
os.system("./built_in_font_gen.py --size 16 -o lv_font_montserrat_16.c --bpp 4")
|
||||
|
||||
print("\nGenerating 18 px")
|
||||
os.system("./built_in_font_gen.py --size 18 -o lv_font_montserrat_18.c --bpp 4")
|
||||
|
||||
print("\nGenerating 20 px")
|
||||
os.system("./built_in_font_gen.py --size 20 -o lv_font_montserrat_20.c --bpp 4")
|
||||
|
||||
print("\nGenerating 22 px")
|
||||
os.system("./built_in_font_gen.py --size 22 -o lv_font_montserrat_22.c --bpp 4")
|
||||
|
||||
print("\nGenerating 24 px")
|
||||
os.system("./built_in_font_gen.py --size 24 -o lv_font_montserrat_24.c --bpp 4")
|
||||
|
||||
print("\nGenerating 26 px")
|
||||
os.system("./built_in_font_gen.py --size 26 -o lv_font_montserrat_26.c --bpp 4")
|
||||
|
||||
print("\nGenerating 28 px")
|
||||
os.system("./built_in_font_gen.py --size 28 -o lv_font_montserrat_28.c --bpp 4")
|
||||
|
||||
print("\nGenerating 30 px")
|
||||
os.system("./built_in_font_gen.py --size 30 -o lv_font_montserrat_30.c --bpp 4")
|
||||
|
||||
print("\nGenerating 32 px")
|
||||
os.system("./built_in_font_gen.py --size 32 -o lv_font_montserrat_32.c --bpp 4")
|
||||
|
||||
print("\nGenerating 34 px")
|
||||
os.system("./built_in_font_gen.py --size 34 -o lv_font_montserrat_34.c --bpp 4")
|
||||
|
||||
print("\nGenerating 36 px")
|
||||
os.system("./built_in_font_gen.py --size 36 -o lv_font_montserrat_36.c --bpp 4")
|
||||
|
||||
print("\nGenerating 38 px")
|
||||
os.system("./built_in_font_gen.py --size 38 -o lv_font_montserrat_38.c --bpp 4")
|
||||
|
||||
print("\nGenerating 40 px")
|
||||
os.system("./built_in_font_gen.py --size 40 -o lv_font_montserrat_40.c --bpp 4")
|
||||
|
||||
print("\nGenerating 42 px")
|
||||
os.system("./built_in_font_gen.py --size 42 -o lv_font_montserrat_42.c --bpp 4")
|
||||
|
||||
print("\nGenerating 44 px")
|
||||
os.system("./built_in_font_gen.py --size 44 -o lv_font_montserrat_44.c --bpp 4")
|
||||
|
||||
print("\nGenerating 46 px")
|
||||
os.system("./built_in_font_gen.py --size 46 -o lv_font_montserrat_46.c --bpp 4")
|
||||
|
||||
print("\nGenerating 48 px")
|
||||
os.system("./built_in_font_gen.py --size 48 -o lv_font_montserrat_48.c --bpp 4")
|
||||
|
||||
print("\nGenerating 12 px subpx")
|
||||
os.system("./built_in_font_gen.py --size 12 -o lv_font_montserrat_12_subpx.c --bpp 4 --subpx")
|
||||
|
||||
print("\nGenerating 28 px compressed")
|
||||
os.system("./built_in_font_gen.py --size 28 -o lv_font_montserrat_28_compressed.c --bpp 4 --compressed")
|
||||
|
||||
print("\nGenerating 16 px Hebrew, Persian")
|
||||
os.system("./built_in_font_gen.py --size 16 -o lv_font_dejavu_16_persian_hebrew.c --bpp 4 --font DejaVuSans.ttf -r 0x20-0x7f,0x5d0-0x5ea,0x600-0x6FF,0xFB50-0xFDFF,0xFE70-0xFEFF")
|
||||
|
||||
print("\nGenerating 14 px CJK")
|
||||
os.system(u"./built_in_font_gen.py --size 14 -o lv_font_source_han_sans_sc_14_cjk.c --bpp 4 --font SourceHanSansSC-Normal.otf -r 0x20-0x7f --symbols (),盗提陽帯鼻画輕ッ冊ェル写父ぁフ結想正四O夫源庭場天續鳥れ講猿苦階給了製守8祝己妳薄泣塩帰ぺ吃変輪那着仍嗯爭熱創味保字宿捨準查達肯ァ薬得査障該降察ね網加昼料等図邪秋コ態品屬久原殊候路願楽確針上被怕悲風份重歡っ附ぷ既4黨價娘朝凍僅際洋止右航よ专角應酸師個比則響健昇豐筆歷適修據細忙跟管長令家ザ期般花越ミ域泳通些油乏ラ。營ス返調農叫樹刊愛間包知把ヤ貧橋拡普聞前ジ建当繰ネ送習渇用補ィ覺體法遊宙ョ酔余利壊語くつ払皆時辺追奇そ們只胸械勝住全沈力光ん深溝二類北面社值試9和五勵ゃ貿幾逐打課ゲて領3鼓辦発評1渉詳暇込计駄供嘛郵頃腦反構絵お容規借身妻国慮剛急乗静必議置克土オ乎荷更肉還混古渡授合主離條値決季晴東大尚央州が嗎験流先医亦林田星晩拿60旅婦量為痛テ孫う環友況玩務其ぼち揺坐一肩腰犯タょ希即果ぶ物練待み高九找やヶ都グ去」サ、气仮雑酒許終企笑録形リ銀切ギ快問滿役単黄集森毎實研喜蘇司鉛洲川条媽ノ才兩話言雖媒出客づ卻現異故り誌逮同訊已視本題ぞを横開音第席費持眾怎選元退限ー賽処喝就残無いガ多ケ沒義遠歌隣錢某雪析嬉採自透き側員予ゼ白婚电へ顯呀始均畫似懸格車騒度わ親店週維億締慣免帳電甚來園浴ゅ愈京と杯各海怒ぜ排敗挙老買7極模実紀ヒ携隻告シ並屋這孩讓質ワブ富賃争康由辞マ火於短樣削弟材注節另室ダ招擁ぃ若套底波行勤關著泊背疲狭作念推ぐ民貸祖介說ビ代温契你我レ入描變再札ソ派頭智遅私聽舉灣山伸放直安ト誕煙付符幅ふ絡她届耳飲忘参革團仕様載ど歩獲嫌息の汚交興魚指資雙與館初学年幸史位柱族走括び考青也共腕Lで販擔理病イ今逃當寺猫邊菓係ム秘示解池影ド文例斷曾事茶寫明科桃藝売便え導禁財飛替而亡到し具空寝辛業ウ府セ國何基菜厳市努張缺雲根外だ断万砂ゴ超使台实ぽ礼最慧算軟界段律像夕丈窓助刻月夏政呼ぴざ擇趣除動従涼方勉名線対存請子氏將5少否諸論美感或西者定食御表は參歳緑命進易性錯房も捕皿判中觀戦ニ緩町ピ番ず金千ろ?不た象治関ャ每看徒卒統じ手範訪押座步号ベ旁以母すほ密減成往歲件緒読歯效院种七謂凝濃嵌震喉繼クュ拭死円2積水欲如ポにさ寒道區精啦姐ア聯能足及停思壓2春且メ裏株官答概黒過氷柿戻厚ぱ党祭織引計け委暗複誘港バ失下村較続神ぇ尤強秀膝兒来績十書済化服破新廠1紹您情半式產系好教暑早め樂地休協良な哪常要揮周かエ麗境働避護ンツ香夜太見設非改広聲他検求危清彼經未在起葉控靴所差內造寄南望尺換向展備眠點完約ぎ裡分説申童優伝島机須塊日立拉,鉄軽單気信很転識支布数紙此迎受心輸坊モ處「訳三曇兄野顔戰增ナ伊列又髪両有取左毛至困吧昔赤狀相夠整別士経頼然簡ホ会發隨営需脱ヨば接永居冬迫圍甘醫誰部充消連弱宇會咲覚姉麼的増首统帶糖朋術商担移景功育庫曲總劃牛程駅犬報ロ學責因パ嚴八世後平負公げ曜陸專午之閉ぬ談ご災昨冷職悪謝對它近射敢意運船臉局難什産頗!球真記ま但蔵究制機案湖臺ひ害券男留内木驗雨施種特復句末濟キ色訴依せ百型る石牠討呢时任執飯歐宅組傳配小活ゆべ暖ズ漸站素らボ束価チ浅回女片独妹英目從認生違策僕楚ペ米こ掛む爸六状落漢プ投カ校做啊洗声探あ割体項履触々訓技ハ低工映是標速善点人デ口次可廿节宵植树端阳旦腊妇费愚劳动儿军师庆圣诞闰".encode('utf-8'))
|
||||
|
||||
print("\nGenerating 16 px CJK")
|
||||
os.system(u"./built_in_font_gen.py --size 16 -o lv_font_source_han_sans_sc_16_cjk.c --bpp 4 --font SourceHanSansSC-Normal.otf -r 0x20-0x7f --symbols (),盗提陽帯鼻画輕ッ冊ェル写父ぁフ結想正四O夫源庭場天續鳥れ講猿苦階給了製守8祝己妳薄泣塩帰ぺ吃変輪那着仍嗯爭熱創味保字宿捨準查達肯ァ薬得査障該降察ね網加昼料等図邪秋コ態品屬久原殊候路願楽確針上被怕悲風份重歡っ附ぷ既4黨價娘朝凍僅際洋止右航よ专角應酸師個比則響健昇豐筆歷適修據細忙跟管長令家ザ期般花越ミ域泳通些油乏ラ。營ス返調農叫樹刊愛間包知把ヤ貧橋拡普聞前ジ建当繰ネ送習渇用補ィ覺體法遊宙ョ酔余利壊語くつ払皆時辺追奇そ們只胸械勝住全沈力光ん深溝二類北面社值試9和五勵ゃ貿幾逐打課ゲて領3鼓辦発評1渉詳暇込计駄供嘛郵頃腦反構絵お容規借身妻国慮剛急乗静必議置克土オ乎荷更肉還混古渡授合主離條値決季晴東大尚央州が嗎験流先医亦林田星晩拿60旅婦量為痛テ孫う環友況玩務其ぼち揺坐一肩腰犯タょ希即果ぶ物練待み高九找やヶ都グ去」サ、气仮雑酒許終企笑録形リ銀切ギ快問滿役単黄集森毎實研喜蘇司鉛洲川条媽ノ才兩話言雖媒出客づ卻現異故り誌逮同訊已視本題ぞを横開音第席費持眾怎選元退限ー賽処喝就残無いガ多ケ沒義遠歌隣錢某雪析嬉採自透き側員予ゼ白婚电へ顯呀始均畫似懸格車騒度わ親店週維億締慣免帳電甚來園浴ゅ愈京と杯各海怒ぜ排敗挙老買7極模実紀ヒ携隻告シ並屋這孩讓質ワブ富賃争康由辞マ火於短樣削弟材注節另室ダ招擁ぃ若套底波行勤關著泊背疲狭作念推ぐ民貸祖介說ビ代温契你我レ入描變再札ソ派頭智遅私聽舉灣山伸放直安ト誕煙付符幅ふ絡她届耳飲忘参革團仕様載ど歩獲嫌息の汚交興魚指資雙與館初学年幸史位柱族走括び考青也共腕Lで販擔理病イ今逃當寺猫邊菓係ム秘示解池影ド文例斷曾事茶寫明科桃藝売便え導禁財飛替而亡到し具空寝辛業ウ府セ國何基菜厳市努張缺雲根外だ断万砂ゴ超使台实ぽ礼最慧算軟界段律像夕丈窓助刻月夏政呼ぴざ擇趣除動従涼方勉名線対存請子氏將5少否諸論美感或西者定食御表は參歳緑命進易性錯房も捕皿判中觀戦ニ緩町ピ番ず金千ろ?不た象治関ャ每看徒卒統じ手範訪押座步号ベ旁以母すほ密減成往歲件緒読歯效院种七謂凝濃嵌震喉繼クュ拭死円2積水欲如ポにさ寒道區精啦姐ア聯能足及停思壓2春且メ裏株官答概黒過氷柿戻厚ぱ党祭織引計け委暗複誘港バ失下村較続神ぇ尤強秀膝兒来績十書済化服破新廠1紹您情半式產系好教暑早め樂地休協良な哪常要揮周かエ麗境働避護ンツ香夜太見設非改広聲他検求危清彼經未在起葉控靴所差內造寄南望尺換向展備眠點完約ぎ裡分説申童優伝島机須塊日立拉,鉄軽單気信很転識支布数紙此迎受心輸坊モ處「訳三曇兄野顔戰增ナ伊列又髪両有取左毛至困吧昔赤狀相夠整別士経頼然簡ホ会發隨営需脱ヨば接永居冬迫圍甘醫誰部充消連弱宇會咲覚姉麼的増首统帶糖朋術商担移景功育庫曲總劃牛程駅犬報ロ學責因パ嚴八世後平負公げ曜陸專午之閉ぬ談ご災昨冷職悪謝對它近射敢意運船臉局難什産頗!球真記ま但蔵究制機案湖臺ひ害券男留内木驗雨施種特復句末濟キ色訴依せ百型る石牠討呢时任執飯歐宅組傳配小活ゆべ暖ズ漸站素らボ束価チ浅回女片独妹英目從認生違策僕楚ペ米こ掛む爸六状落漢プ投カ校做啊洗声探あ割体項履触々訓技ハ低工映是標速善点人デ口次可廿节宵植树端阳旦腊妇费愚劳动儿军师庆圣诞闰".encode('utf-8'))
|
||||
|
||||
print("\nGenerating 8 px unscii")
|
||||
os.system("lv_font_conv --no-compress --no-prefilter --bpp 1 --size 8 --font unscii-8.ttf -r 0x20-0x7F --format lvgl -o lv_font_unscii_8.c --force-fast-kern-format")
|
||||
|
||||
print("\nGenerating 16 px unscii")
|
||||
os.system("lv_font_conv --no-compress --no-prefilter --bpp 1 --size 16 --font unscii-8.ttf -r 0x20-0x7F --format lvgl -o lv_font_unscii_16.c --force-fast-kern-format")
|
||||
|
||||
os.system('sed -i \'s|#include "lvgl/lvgl.h"|#include "../../lvgl.h"|\' lv_font_*.c')
|
||||
os.system('astyle --ignore-exclude-errors --options=../code-format.cfg "lv_font_*.c"')
|
||||
os.system('mv lv_font_*.c ../../src/font/')
|
||||
BIN
managed_components/lvgl__lvgl/scripts/built_in_font/unscii-8.ttf
Normal file
BIN
managed_components/lvgl__lvgl/scripts/built_in_font/unscii-8.ttf
Normal file
Binary file not shown.
122
managed_components/lvgl__lvgl/scripts/changelog-template.hbs
Normal file
122
managed_components/lvgl__lvgl/scripts/changelog-template.hbs
Normal file
@@ -0,0 +1,122 @@
|
||||
{{#each releases}}
|
||||
`{{title}} <{{href}}>`__ {{niceDate}}
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Breaking Changes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='BREAKING CHANGE'}}
|
||||
- .. warning: {{message}} `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='BREAKING CHANGE'}}
|
||||
- .. warning: {{subject}} `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='BREAKING CHANGE'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Architectural
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^arch' exclude='BREAKING CHANGE'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^arch' exclude='BREAKING CHANGE'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^arch' exclude='BREAKING CHANGE'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^feat' exclude='BREAKING CHANGE'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^feat' exclude='BREAKING CHANGE'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^feat' exclude='BREAKING CHANGE'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^perf' exclude='BREAKING CHANGE'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^perf' exclude='BREAKING CHANGE'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^perf' exclude='BREAKING CHANGE'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Fixes
|
||||
~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^fix' exclude='(^fix conflict|^fix warning|BREAKING CHANGE)'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^fix' exclude='(^fix conflict|^fix warning|BREAKING CHANGE)'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^fix' exclude='(^fix conflict|^fix warning|BREAKING CHANGE)'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^example'}}
|
||||
- **{{message}}** `{{id}} <({{href}})>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^example'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^example'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Docs
|
||||
~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='^docs'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='^docs'}}
|
||||
- **{{subject}}** `{{shorthash}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='^docs'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
CI and tests
|
||||
~~~~~~~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' message='(^ci|^test)'}}
|
||||
- **{{message}}** `{{id}} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' message='(^ci|^test)'}}
|
||||
- **{{subject}}** `{{shorthash }} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' message='(^ci|^test)'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
Others
|
||||
~~~~~~
|
||||
|
||||
{{#commit-list merges heading='' exclude='(^fix|^feat|^perf|^docs|^example|^ci|^test)'}}
|
||||
- **{{message}}** `{{id }} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list commits heading='' exclude='(^fix|^feat|^perf|^docs|^example|^ci|^test)'}}
|
||||
- **{{subject}}** `{{shorthash }} <{{href}}>`__
|
||||
{{/commit-list}}
|
||||
{{#commit-list fixes heading='' exclude='(^fix|^feat|^perf|^docs|^example|^ci|^test)'}}
|
||||
- **{{commit.subject}}** `{{commit.shorthash}} <{{commit.href}}>`__
|
||||
{{/commit-list}}
|
||||
|
||||
{{/each}}
|
||||
25
managed_components/lvgl__lvgl/scripts/changelog_gen.sh
Normal file
25
managed_components/lvgl__lvgl/scripts/changelog_gen.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Generate CHANGELOG_LAST.md from changes since the last version tag. (vx.y.z-dev tags are ignored)
|
||||
# CHANGELOG_LAST.md can be edited manually if required and manually added to docs/CHANGELOG.md
|
||||
#
|
||||
# Requirements:
|
||||
# npm install -g auto-changelog
|
||||
#
|
||||
# Usage:
|
||||
# changelog-gen <next-version>
|
||||
#
|
||||
# Example: if the latest version is v5.6.7 the following can be used for bugfix, minor or major versions:
|
||||
# changelog-gen v5.6.8
|
||||
# changelog-gen v5.7.0
|
||||
# changelog-gen v6.0.0
|
||||
|
||||
OUTPUT_NAME=CHANGELOG_LAST.rst
|
||||
auto-changelog -t changelog-template.hbs -l false --latest-version $1 --unreleased-only --tag-pattern ^v[0-9]+.[0-9]+.[0-9]+$ --merge-url "https://github.com/lvgl/lvgl/pull/{id}" -o $OUTPUT_NAME
|
||||
|
||||
# Remove leading space from PR commit name
|
||||
# Eg "- ** PR TITLE" becomes "- **PR TITLE"
|
||||
sed -i 's/- \*\* */- \*\*/g' $OUTPUT_NAME
|
||||
|
||||
# Remove trailing space from PR commit name
|
||||
# Eg "- **PR TITLE **" becomes "- **PR TITLE**"
|
||||
sed -i 's/ \*\* /\*\* /g' $OUTPUT_NAME
|
||||
396
managed_components/lvgl__lvgl/scripts/check_gcov_coverage.py
Normal file
396
managed_components/lvgl__lvgl/scripts/check_gcov_coverage.py
Normal file
@@ -0,0 +1,396 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check code coverage for new lines in a git commit using gcovr
|
||||
"""
|
||||
import argparse
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
from typing import Dict, Set, Tuple, List, Optional
|
||||
|
||||
|
||||
def create_argument_parser() -> argparse.ArgumentParser:
|
||||
"""Create argument parser"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Check gcov coverage for new code in a git commit",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--commit",
|
||||
help="Git commit hash, reference (e.g. HEAD, HEAD~1) or range (base...head)",
|
||||
default="HEAD",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--path",
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"Analyze coverage for a single file or a directory (relative or absolute path). "
|
||||
"When specified, --commit is ignored."
|
||||
),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--fail-under",
|
||||
type=float,
|
||||
metavar="PERCENT",
|
||||
default=0,
|
||||
help="Fail if coverage is below this percentage (0-100), default: 0 (no failure)",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def run_git_command(args: List[str], cwd: str = ".") -> str:
|
||||
"""Run git command and return output"""
|
||||
result = subprocess.run(
|
||||
["git"] + args, capture_output=True, text=True, cwd=cwd, check=True
|
||||
)
|
||||
return result.stdout
|
||||
|
||||
|
||||
def get_changed_lines(commit: str, root: str) -> Dict[str, Set[int]]:
|
||||
"""
|
||||
Get changed lines in specified commit
|
||||
Returns: {file_path: {set of changed line numbers}}
|
||||
"""
|
||||
diff_output = run_git_command(
|
||||
["diff", f"{commit}^", commit, "--unified=0"], cwd=root
|
||||
)
|
||||
|
||||
changed_lines: Dict[str, Set[int]] = {}
|
||||
current_file = None
|
||||
|
||||
for line in diff_output.split("\n"):
|
||||
if line.startswith("+++ b/"):
|
||||
current_file = line[6:]
|
||||
if current_file not in changed_lines:
|
||||
changed_lines[current_file] = set()
|
||||
|
||||
elif line.startswith("@@"):
|
||||
match = re.search(r"\+(\d+)(?:,(\d+))?", line)
|
||||
if match and current_file:
|
||||
start_line = int(match.group(1))
|
||||
count = int(match.group(2)) if match.group(2) else 1
|
||||
for lineno in range(start_line, start_line + count):
|
||||
changed_lines[current_file].add(lineno)
|
||||
|
||||
return changed_lines
|
||||
|
||||
|
||||
def get_coverage_data(root: str) -> Dict[str, Dict[int, int]]:
|
||||
"""
|
||||
Get coverage data using gcovr
|
||||
Returns: {rel_file_path: {line_number: execution_count}}
|
||||
Notes:
|
||||
- Only lines explicitly present in gcovr JSON are considered "coverable".
|
||||
Lines that are missing from the JSON (e.g. preprocessor directives like
|
||||
#include, comments, whitespace, or excluded lines) are treated as
|
||||
non-coverable and will be ignored by the uncovered check.
|
||||
Raises: subprocess.CalledProcessError if gcovr fails
|
||||
"""
|
||||
filter_pattern = os.path.join(root, r"src/(?:.*/)?lv_.*\.c")
|
||||
cmd = [
|
||||
"gcovr",
|
||||
"--gcov-ignore-parse-errors",
|
||||
"--json",
|
||||
"-",
|
||||
"--root",
|
||||
root,
|
||||
"--filter",
|
||||
filter_pattern,
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=root,
|
||||
check=False, # Don't raise exception automatically
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"gcovr command failed (exit code {result.returncode}):\n"
|
||||
if result.stderr:
|
||||
error_msg += f"Error output:\n{result.stderr}\n"
|
||||
if result.stdout:
|
||||
error_msg += f"Standard output:\n{result.stdout}\n"
|
||||
raise subprocess.CalledProcessError(
|
||||
result.returncode, cmd, result.stdout, result.stderr
|
||||
)
|
||||
|
||||
coverage_json = json.loads(result.stdout)
|
||||
coverage_data: Dict[str, Dict[int, int]] = {}
|
||||
|
||||
for file_info in coverage_json.get("files", []):
|
||||
# Normalize path to be relative to repo root with POSIX separators.
|
||||
filename = file_info["file"]
|
||||
rel = os.path.relpath(filename, root)
|
||||
rel = rel.replace(os.path.sep, "/")
|
||||
coverage_data[rel] = {}
|
||||
|
||||
for line_info in file_info.get("lines", []):
|
||||
line_number = line_info.get("line_number")
|
||||
# Only consider lines explicitly listed by gcovr as coverable.
|
||||
# Some gcovr versions may include additional flags like
|
||||
# "gcovr/noncode" or "excluded"; we simply ignore such lines
|
||||
# by relying on their absence from the JSON or by requiring
|
||||
# a numeric execution count.
|
||||
if line_number is None:
|
||||
continue
|
||||
count = line_info.get("count")
|
||||
if isinstance(count, int):
|
||||
coverage_data[rel][line_number] = count
|
||||
|
||||
return coverage_data
|
||||
|
||||
|
||||
def check_commit_coverage(
|
||||
commit: str, root: str
|
||||
) -> Tuple[int, int, List[Tuple[str, int]], int]:
|
||||
"""
|
||||
Check coverage for a commit or range
|
||||
Returns: (covered_lines, total_new_lines, uncovered_lines, skipped_noncoverable)
|
||||
"""
|
||||
|
||||
if "..." in commit:
|
||||
# Handle range format (base...head)
|
||||
base, head = commit.split("...")
|
||||
# Get changes for each commit in the range
|
||||
commits = run_git_command(["rev-list", f"{base}...{head}"], cwd=root).split()
|
||||
else:
|
||||
# Handle single commit
|
||||
commits = [commit]
|
||||
|
||||
print(f"Found {len(commits)} commits in range:")
|
||||
changed_lines = {}
|
||||
for c in commits:
|
||||
print(f" {c}")
|
||||
cl = get_changed_lines(c, root)
|
||||
for f, lines in cl.items():
|
||||
if f not in changed_lines:
|
||||
changed_lines[f] = set()
|
||||
changed_lines[f].update(lines)
|
||||
|
||||
print(f"Found changes in {len(changed_lines)} files (before filtering):")
|
||||
for filename, lines in sorted(changed_lines.items()):
|
||||
print(f" {filename}: {len(lines)} lines changed")
|
||||
|
||||
print("Getting coverage data...")
|
||||
coverage_data = get_coverage_data(root)
|
||||
|
||||
# Normalize changed file paths to POSIX separators for matching.
|
||||
normalized_changed: Dict[str, Set[int]] = {
|
||||
f.replace(os.path.sep, "/"): lines for f, lines in changed_lines.items()
|
||||
}
|
||||
|
||||
# If desired, we could additionally filter by the gcovr filter pattern.
|
||||
# However, by intersecting with coverage_data keys, we inherently ignore
|
||||
# files that are not part of coverage anyway.
|
||||
|
||||
total_new_lines = 0 # total coverable new lines (per gcovr)
|
||||
covered_lines = 0
|
||||
uncovered_lines: List[Tuple[str, int]] = []
|
||||
skipped_noncoverable = 0 # changed lines that gcovr doesn't consider coverable
|
||||
|
||||
# Build quick lookup for filenames present in coverage.
|
||||
coverage_files: Set[str] = set(coverage_data.keys())
|
||||
|
||||
# Iterate changed files and intersect with gcovr-provided coverable lines.
|
||||
for filename, line_numbers in normalized_changed.items():
|
||||
if filename not in coverage_files:
|
||||
# Entire file has no coverable lines in gcovr output; skip all.
|
||||
skipped_noncoverable += len(line_numbers)
|
||||
continue
|
||||
|
||||
file_coverage = coverage_data[filename]
|
||||
for lineno in line_numbers:
|
||||
if lineno not in file_coverage:
|
||||
skipped_noncoverable += 1
|
||||
continue
|
||||
|
||||
total_new_lines += 1
|
||||
if file_coverage[lineno] > 0:
|
||||
covered_lines += 1
|
||||
else:
|
||||
uncovered_lines.append((filename, lineno))
|
||||
|
||||
return covered_lines, total_new_lines, uncovered_lines, skipped_noncoverable
|
||||
|
||||
|
||||
def check_path_coverage(path: str, root: str) -> Tuple[int, int, List[Tuple[str, int]]]:
|
||||
"""
|
||||
Compute coverage for a specific file or directory.
|
||||
Returns: (covered_lines, total_coverable_lines, uncovered_lines)
|
||||
Notes:
|
||||
- Only lines reported by gcovr as coverable are counted toward totals.
|
||||
- If PATH is a directory, coverage is aggregated over all coverable files within it.
|
||||
- If PATH is a file, coverage is computed only for that file.
|
||||
"""
|
||||
# Normalize input path
|
||||
abs_path = os.path.abspath(path)
|
||||
if not os.path.exists(abs_path):
|
||||
print(f"Error: The specified path does not exist: {abs_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Ensure we operate from repo root to construct relative POSIX paths
|
||||
root = os.path.abspath(root)
|
||||
|
||||
# Build relative POSIX target(s)
|
||||
rel = os.path.relpath(abs_path, root)
|
||||
rel_posix = rel.replace(os.path.sep, "/")
|
||||
|
||||
print("Getting coverage data...")
|
||||
coverage_data = get_coverage_data(root)
|
||||
|
||||
covered = 0
|
||||
total = 0
|
||||
uncovered: List[Tuple[str, int]] = []
|
||||
|
||||
if os.path.isdir(abs_path):
|
||||
# Directory scope: include all files under this directory
|
||||
scoped_files = {}
|
||||
for f, lines in coverage_data.items():
|
||||
# Convert both to absolute paths for comparison
|
||||
f_abs = os.path.abspath(os.path.join(root, f.replace("/", os.path.sep)))
|
||||
# If the common path of abs_path and f_abs is abs_path, f is under abs_path
|
||||
if os.path.commonpath([abs_path, f_abs]) == abs_path:
|
||||
scoped_files[f] = lines
|
||||
|
||||
print(f"Found {len(scoped_files)} coverable file(s) under: {rel_posix}")
|
||||
for filename, line_map in sorted(scoped_files.items()):
|
||||
for lineno, count in line_map.items():
|
||||
total += 1
|
||||
if count > 0:
|
||||
covered += 1
|
||||
else:
|
||||
uncovered.append((filename, lineno))
|
||||
else:
|
||||
# File scope
|
||||
if rel_posix in coverage_data:
|
||||
line_map = coverage_data[rel_posix]
|
||||
print(f"Found coverable file: {rel_posix}")
|
||||
for lineno, count in line_map.items():
|
||||
total += 1
|
||||
if count > 0:
|
||||
covered += 1
|
||||
else:
|
||||
uncovered.append((rel_posix, lineno))
|
||||
else:
|
||||
print(
|
||||
f"Warning: No coverable lines found for '{rel_posix}' in gcovr output."
|
||||
)
|
||||
|
||||
return covered, total, uncovered
|
||||
|
||||
|
||||
def report_coverage(
|
||||
header: str,
|
||||
total: int,
|
||||
covered: int,
|
||||
uncovered: List[Tuple[str, int]],
|
||||
fail_under: float,
|
||||
*,
|
||||
total_label: str,
|
||||
skipped_noncoverable: Optional[int] = None,
|
||||
) -> int:
|
||||
"""
|
||||
Print a standardized coverage report and return exit code (0/1).
|
||||
|
||||
- header: text to show in the report title (e.g., "commit <hash>", "'<path>'")
|
||||
- total_label: label to use for the total line count (e.g.,
|
||||
"New coverable lines (per gcovr)" for commit mode, or
|
||||
"Coverable lines (per gcovr)" for path mode)
|
||||
- skipped_noncoverable: when provided, prints the skipped non-coverable count
|
||||
"""
|
||||
|
||||
title = f" Coverage analysis results for {header} "
|
||||
separator = "=" * len(title)
|
||||
print(f"\n{separator}\n{title}\n{separator}")
|
||||
print(f"{total_label}: {total}")
|
||||
print(f"Covered lines: {covered}")
|
||||
print(f"Uncovered lines: {len(uncovered)}")
|
||||
if skipped_noncoverable is not None:
|
||||
print(f"Skipped non-coverable changed lines: {skipped_noncoverable}")
|
||||
|
||||
retval = 0
|
||||
if total > 0:
|
||||
coverage_percent = (covered / total) * 100
|
||||
print(f"Coverage: {coverage_percent:.2f}%")
|
||||
if coverage_percent < fail_under:
|
||||
print(
|
||||
f"\n✗ Coverage {coverage_percent:.2f}% is below required {fail_under}%"
|
||||
)
|
||||
retval = 1
|
||||
|
||||
if uncovered:
|
||||
print(
|
||||
"\nUncovered lines (explicitly reported by gcovr as coverable with 0 hits):"
|
||||
)
|
||||
for filename, lineno in sorted(uncovered):
|
||||
print(f" {filename}:{lineno}")
|
||||
else:
|
||||
if total == 0:
|
||||
print("\nNo coverable lines found.")
|
||||
else:
|
||||
print(f"\n✓ Code coverage check passed!")
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Main entry point"""
|
||||
parser = create_argument_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
root = os.path.dirname(script_dir)
|
||||
os.chdir(root)
|
||||
print(f"Current working directory: {root}")
|
||||
|
||||
if args.path:
|
||||
# Path mode: ignore commit, compute coverage for file/dir
|
||||
covered, total, uncovered = check_path_coverage(args.path, root)
|
||||
|
||||
return report_coverage(
|
||||
header=f"'{args.path}'",
|
||||
total=total,
|
||||
covered=covered,
|
||||
uncovered=uncovered,
|
||||
fail_under=args.fail_under,
|
||||
total_label="Coverable lines (per gcovr)",
|
||||
)
|
||||
else:
|
||||
# Commit mode: default behavior
|
||||
covered, total, uncovered, skipped_noncoverable = check_commit_coverage(
|
||||
args.commit, root
|
||||
)
|
||||
|
||||
return report_coverage(
|
||||
header=f"commit {args.commit}",
|
||||
total=total,
|
||||
covered=covered,
|
||||
uncovered=uncovered,
|
||||
fail_under=args.fail_under,
|
||||
total_label="New coverable lines (per gcovr)",
|
||||
skipped_noncoverable=skipped_noncoverable,
|
||||
)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Command failed - {e}", file=sys.stderr)
|
||||
if e.stderr:
|
||||
print(f"Error details:\n{e.stderr}", file=sys.stderr)
|
||||
if e.stdout:
|
||||
print(f"Command output:\n{e.stdout}", file=sys.stderr)
|
||||
return e.returncode
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
65
managed_components/lvgl__lvgl/scripts/code-format.cfg
Normal file
65
managed_components/lvgl__lvgl/scripts/code-format.cfg
Normal file
@@ -0,0 +1,65 @@
|
||||
--style=kr
|
||||
--indent=spaces=4
|
||||
--indent-classes
|
||||
--indent-switches
|
||||
--indent-cases
|
||||
--indent-preproc-block
|
||||
--indent-preproc-define
|
||||
--indent-col1-comments
|
||||
--pad-oper
|
||||
--unpad-paren
|
||||
--align-pointer=middle
|
||||
--align-reference=middle
|
||||
--convert-tabs
|
||||
--max-code-length=120
|
||||
--break-after-logical
|
||||
--break-closing-braces
|
||||
--attach-closing-while
|
||||
--min-conditional-indent=0
|
||||
--max-continuation-indent=120
|
||||
--mode=c
|
||||
# Allows each platform to use its own line endings. This then does
|
||||
# what Git does out of the box: uses platform line endings in the
|
||||
# working directory, but uses only LF line endings in the repository.
|
||||
# `astyle` also natively uses line the endings it finds in the files,
|
||||
# so this will be LF-only on Linux and CR/LF on Windows, in alignment
|
||||
# with Git's default behavior. This prevents `astyle` from modifying
|
||||
# every source file, which Git perceives as a change on Windows platforms.
|
||||
#--lineend=linux
|
||||
--suffix=none
|
||||
--preserve-date
|
||||
--formatted
|
||||
--ignore-exclude-errors
|
||||
--ignore-exclude-errors-x
|
||||
--exclude=assets
|
||||
--exclude=test_assets
|
||||
--exclude=test_fonts
|
||||
--exclude=../src/lv_conf_internal.h
|
||||
--exclude=../src/core/lv_obj_style_gen.c
|
||||
--exclude=../src/core/lv_obj_style_gen.h
|
||||
--exclude=../src/libs/gif
|
||||
--exclude=../src/libs/frogfs
|
||||
--exclude=../src/libs/lodepng/lodepng.c
|
||||
--exclude=../src/libs/lodepng/lodepng.h
|
||||
--exclude=../src/libs/qrcode/qrcodegen.c
|
||||
--exclude=../src/libs/qrcode/qrcodegen.h
|
||||
--exclude=../src/libs/tjpgd/tjpgd.c
|
||||
--exclude=../src/libs/tjpgd/tjpgd.h
|
||||
--exclude=../src/libs/tjpgd/tjpgdcnf.h
|
||||
--exclude=../src/libs/thorvg
|
||||
--exclude=../src/libs/expat
|
||||
--exclude=../src/libs/lz4
|
||||
--exclude=../src/libs/FT800-FT813
|
||||
--exclude=../src/drivers/opengles/glad
|
||||
--exclude=../src/others/vg_lite_tvg/vg_lite.h
|
||||
--exclude=../demos/high_res/fonts
|
||||
--exclude=../tests/unity/unity.c
|
||||
--exclude=../tests/unity/unity_internals.h
|
||||
--exclude=../tests/unity/unity_support.c
|
||||
--exclude=../tests/unity/unity_support.h
|
||||
--exclude=../tests/test_images
|
||||
--exclude=../tests/build_test_defheap
|
||||
--exclude=../tests/build_test_sysheap
|
||||
--exclude=../tests/build_test_vg_lite
|
||||
--exclude=../tests/build_test_perf32b
|
||||
--exclude=../tests/build_test_perf64b
|
||||
65
managed_components/lvgl__lvgl/scripts/code-format.py
Normal file
65
managed_components/lvgl__lvgl/scripts/code-format.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Argument enhancement: to only run `astyle` on a specified directory, to
|
||||
# only include changed source code, these arguments have been added. If
|
||||
# run with no arguments, all the normal directories are examined as before:
|
||||
# - /demos/
|
||||
# - /examples/
|
||||
# - /src/
|
||||
# - /tests/
|
||||
#
|
||||
# Args:
|
||||
# If ANY args are specified, ONLY run `astyle` on the specified directories.
|
||||
# Any combination can be specified.
|
||||
include_demos = True
|
||||
include_examples = True
|
||||
include_src = True
|
||||
include_tests = True
|
||||
|
||||
args = sys.argv[1:]
|
||||
|
||||
# Have any args been specified?
|
||||
if args:
|
||||
include_demos = False
|
||||
include_examples = False
|
||||
include_src = False
|
||||
include_tests = False
|
||||
|
||||
for arg in args:
|
||||
if arg == "demos":
|
||||
include_demos = True
|
||||
elif arg == "examples":
|
||||
include_examples = True
|
||||
elif arg == "src":
|
||||
include_src = True
|
||||
elif arg == "tests":
|
||||
include_tests = True
|
||||
else:
|
||||
print(f'Argument [{arg}] not recognized.')
|
||||
print('Usage:')
|
||||
print(' python code-format.py [dir [dir ...]]')
|
||||
print(' where: dir can be demos, examples, src or tests.')
|
||||
exit(1)
|
||||
|
||||
script_dir = os.path.realpath(__file__)
|
||||
script_dir = os.path.dirname(script_dir)
|
||||
cfg_file = os.path.join(script_dir, 'code-format.cfg')
|
||||
|
||||
if include_demos:
|
||||
print("\nFormatting demos")
|
||||
os.system(f'astyle --options={cfg_file} --recursive "{script_dir}/../demos/*.c,*.cpp,*.h"')
|
||||
|
||||
if include_examples:
|
||||
print("\nFormatting examples")
|
||||
os.system(f'astyle --options={cfg_file} --recursive "{script_dir}/../examples/*.c,*.cpp,*.h"')
|
||||
|
||||
if include_src:
|
||||
print("\nFormatting src")
|
||||
os.system(f'astyle --options={cfg_file} --recursive "{script_dir}/../src/*.c,*.cpp,*.h"')
|
||||
|
||||
if include_tests:
|
||||
print("\nFormatting tests")
|
||||
os.system(f'astyle --options={cfg_file} --recursive "{script_dir}/../tests/*.c,*.cpp,*.h"')
|
||||
3
managed_components/lvgl__lvgl/scripts/cppcheck_run.sh
Normal file
3
managed_components/lvgl__lvgl/scripts/cppcheck_run.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cppcheck -j8 --template="{severity}\t{file}:{line}\t{id}: {message}" --enable=all ../src/ --output-file=cppcheck_res.txt --suppress=unusedFunction --suppress=preprocessorErrorDirective --force
|
||||
18
managed_components/lvgl__lvgl/scripts/filetohex.py
Normal file
18
managed_components/lvgl__lvgl/scripts/filetohex.py
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import textwrap
|
||||
import re
|
||||
|
||||
with open(sys.argv[1], 'r') as file:
|
||||
s = file.read()
|
||||
|
||||
b = bytearray()
|
||||
|
||||
if '--filter-character' in sys.argv:
|
||||
s = re.sub(r'[^\x00-\xff]', '', s)
|
||||
if '--null-terminate' in sys.argv:
|
||||
s += '\x00'
|
||||
|
||||
b.extend(map(ord, s))
|
||||
|
||||
print(textwrap.fill(', '.join([hex(a) for a in b]), 96))
|
||||
4
managed_components/lvgl__lvgl/scripts/find_version.sh
Normal file
4
managed_components/lvgl__lvgl/scripts/find_version.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )"
|
||||
sed -n '/LVGL_VERSION_MAJOR/ {N;s@#define \+LVGL_VERSION_M.... \+@@g;s@\n@.@p}' "$SCRIPTPATH/../lv_version.h"
|
||||
114
managed_components/lvgl__lvgl/scripts/font_license_verify.py
Normal file
114
managed_components/lvgl__lvgl/scripts/font_license_verify.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from typing import List, Tuple
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
from fontTools.ttLib import TTFont
|
||||
except ImportError:
|
||||
print("Need fonttools package, do `pip3 install fonttools`")
|
||||
sys.exit(1)
|
||||
|
||||
# Fonts that are excluded from the license check
|
||||
# Only add fonts that are known to be public domain or have a compatible license
|
||||
_EXCLUDED_FONTS = {
|
||||
"OpenTypeTest GPOS One",
|
||||
}
|
||||
|
||||
# Font name mapping to remove any style suffix
|
||||
_FONT_NAME_MAP = {
|
||||
"Montserrat Medium": "Montserrat",
|
||||
"Montserrat SemiBold": "Montserrat",
|
||||
"Montserrat Bold": "Montserrat",
|
||||
"Montserrat Regular": "Montserrat",
|
||||
"Source Han Sans SC Normal": "Source Han Sans SC",
|
||||
"Font Awesome 5 Free Solid": "Font Awesome",
|
||||
"Lato Regular": "Lato",
|
||||
"Noto Sans SC Regular": "Noto Sans SC",
|
||||
}
|
||||
|
||||
|
||||
def get_font_full_name(font_path: str) -> str:
|
||||
font = TTFont(font_path)
|
||||
name_records = font["name"].names
|
||||
fallback = None
|
||||
for record in name_records:
|
||||
if record.nameID == 1: # ID 1 corresponds to the font family name and will be used if full name doesn't exists
|
||||
fallback = record.toStr()
|
||||
if record.nameID == 4: # ID 4 corresponds to the full font name
|
||||
return record.toStr()
|
||||
return fallback
|
||||
|
||||
|
||||
def list_intree_fonts(path: str) -> List[Tuple[str, str]]:
|
||||
fonts = []
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
if file.lower().endswith((".ttf", ".otf", ".woff", ".woff2")):
|
||||
font_path = os.path.join(root, file)
|
||||
font_name = get_font_full_name(font_path).strip()
|
||||
if font_name:
|
||||
# Add a no-strict mode to ignore missing license files
|
||||
fonts.append((font_path, font_name))
|
||||
return fonts
|
||||
|
||||
|
||||
def has_intree_license(license_root_folder: str, font_name: str) -> bool:
|
||||
if font_name in _EXCLUDED_FONTS:
|
||||
return True
|
||||
|
||||
# Prepare candidate folder names, ignoring casing
|
||||
candidates = {
|
||||
font_name.lower(),
|
||||
font_name.replace(" ", "_").lower(),
|
||||
font_name.replace(" ", "").lower(),
|
||||
}
|
||||
|
||||
if font_name in _FONT_NAME_MAP:
|
||||
candidates.add(_FONT_NAME_MAP[font_name].lower())
|
||||
candidates.add(_FONT_NAME_MAP[font_name].replace(" ", "_").lower())
|
||||
candidates.add(_FONT_NAME_MAP[font_name].replace(" ", "").lower())
|
||||
|
||||
# List all directories in the license_root_folder
|
||||
for entry in os.listdir(license_root_folder):
|
||||
entry_path = os.path.join(license_root_folder, entry)
|
||||
if os.path.isdir(entry_path):
|
||||
# Compare the directory name in lowercase with candidates
|
||||
if entry.lower() in candidates:
|
||||
# Check if the directory contains at least one file (ignoring subdirectories)
|
||||
for item in os.listdir(entry_path):
|
||||
item_path = os.path.join(entry_path, item)
|
||||
if os.path.isfile(item_path):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Verify font licenses")
|
||||
parser.add_argument(
|
||||
"--no-strict",
|
||||
action="store_true",
|
||||
help="Ignore missing license files",
|
||||
default=False,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(__file__)
|
||||
# List of font files in the tree
|
||||
fonts = list_intree_fonts(os.path.join(SCRIPT_DIR, ".."))
|
||||
has_font_without_license = False
|
||||
|
||||
print("Verifying license for fonts:")
|
||||
for path, name in fonts:
|
||||
if has_intree_license(
|
||||
os.path.join(SCRIPT_DIR, "built_in_font", "font_license"), name
|
||||
):
|
||||
print(f" [OK] '{name}'")
|
||||
else:
|
||||
print(f" [MISSING] '{name}' - {path}")
|
||||
has_font_without_license = True
|
||||
|
||||
if has_font_without_license and not args.no_strict:
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1 @@
|
||||
3.10
|
||||
37
managed_components/lvgl__lvgl/scripts/gdb/README.md
Normal file
37
managed_components/lvgl__lvgl/scripts/gdb/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# lvglgdb
|
||||
|
||||
lvglgdb is a GDB script for LVGL.
|
||||
|
||||
# Installation
|
||||
|
||||
```bash
|
||||
pip install lvglgdb
|
||||
```
|
||||
|
||||
# Simple Usage
|
||||
|
||||
In your GDB session, run:
|
||||
```bash
|
||||
py import lvglgdb
|
||||
|
||||
dump obj
|
||||
dump cache image
|
||||
dump cache image_header
|
||||
```
|
||||
|
||||
# Structure
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
lvgl["lvgl<br/>(mem→python object)"]
|
||||
gdb_cmds["gdb_cmds<br/>(gdb commands)"]
|
||||
lvglgdb["lvglgdb"]
|
||||
|
||||
lvglgdb --> lvgl
|
||||
lvglgdb --> gdb_cmds
|
||||
gdb_cmds --> lvgl
|
||||
|
||||
classDef pkg fill:white,stroke:gray
|
||||
classDef core fill:white,stroke:gray
|
||||
class lvglgdb,lvgl,gdb_cmds pkg
|
||||
```
|
||||
13
managed_components/lvgl__lvgl/scripts/gdb/gdbinit.py
Normal file
13
managed_components/lvgl__lvgl/scripts/gdb/gdbinit.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
here = path.dirname(path.abspath(__file__))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if here not in sys.path:
|
||||
sys.path.insert(0, here)
|
||||
|
||||
for key in tuple(filter(lambda m: m.startswith("lvglgdb"), sys.modules.keys())):
|
||||
del sys.modules[key]
|
||||
|
||||
import lvglgdb # noqa: F401
|
||||
@@ -0,0 +1,38 @@
|
||||
from .value import Value
|
||||
from .lvgl import (
|
||||
curr_inst,
|
||||
LVDisplay,
|
||||
LVDrawBuf,
|
||||
LVList,
|
||||
LVObject,
|
||||
dump_style_info,
|
||||
LVCache,
|
||||
LVCacheEntry,
|
||||
LVCacheLRURB,
|
||||
LVCacheLRURBIterator,
|
||||
LVCacheIteratorBase,
|
||||
LVImageCache,
|
||||
LVImageHeaderCache,
|
||||
create_cache_iterator,
|
||||
LVRedBlackTree,
|
||||
)
|
||||
from . import cmds as cmds
|
||||
|
||||
__all__ = [
|
||||
"curr_inst",
|
||||
"LVDisplay",
|
||||
"LVDrawBuf",
|
||||
"LVList",
|
||||
"LVCache",
|
||||
"LVRedBlackTree",
|
||||
"LVObject",
|
||||
"dump_style_info",
|
||||
"Value",
|
||||
"LVCacheEntry",
|
||||
"LVCacheLRURB",
|
||||
"LVCacheLRURBIterator",
|
||||
"LVCacheIteratorBase",
|
||||
"LVImageCache",
|
||||
"LVImageHeaderCache",
|
||||
"create_cache_iterator",
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
import gdb
|
||||
|
||||
from .core import DumpObj
|
||||
from .display import DumpDisplayBuf
|
||||
from .draw import InfoDrawUnit
|
||||
from .misc import InfoStyle, DumpCache
|
||||
from .debugger import Debugger
|
||||
from .drivers import Lvglobal
|
||||
|
||||
__all__ = []
|
||||
|
||||
# Set pagination off and python print-stack full
|
||||
gdb.execute("set pagination off")
|
||||
gdb.write("set pagination off\n")
|
||||
gdb.execute("set python print-stack full")
|
||||
gdb.write("set python print-stack full\n")
|
||||
|
||||
|
||||
# Debugger
|
||||
Debugger()
|
||||
|
||||
# Dumps
|
||||
DumpObj()
|
||||
DumpDisplayBuf()
|
||||
DumpCache()
|
||||
|
||||
# Infos
|
||||
InfoStyle()
|
||||
InfoDrawUnit()
|
||||
|
||||
# Drivers
|
||||
Lvglobal()
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lv_obj import DumpObj
|
||||
|
||||
__all__ = [
|
||||
"DumpObj",
|
||||
]
|
||||
@@ -0,0 +1,64 @@
|
||||
import argparse
|
||||
import gdb
|
||||
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
from lvglgdb.lvgl import LVObject, dump_obj_info
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class DumpObj(gdb.Command):
|
||||
"""dump obj tree from specified obj"""
|
||||
|
||||
def __init__(self):
|
||||
super(DumpObj, self).__init__(
|
||||
"dump obj", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def dump_obj(self, obj: LVObject, depth=0, limit=None):
|
||||
if not obj:
|
||||
return
|
||||
|
||||
# dump self
|
||||
print(" " * depth, end="")
|
||||
dump_obj_info(obj)
|
||||
|
||||
if limit is not None and depth >= limit:
|
||||
return
|
||||
|
||||
# dump children
|
||||
for child in obj.children:
|
||||
self.dump_obj(child, depth + 1, limit=limit)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description="Dump lvgl obj tree.")
|
||||
parser.add_argument(
|
||||
"-L",
|
||||
"--level",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Limit the depth of the tree.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"root",
|
||||
type=str,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Optional root obj to dump.",
|
||||
)
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
if args.root:
|
||||
root = gdb.parse_and_eval(args.root)
|
||||
root = LVObject(Value(root))
|
||||
self.dump_obj(root, limit=args.level)
|
||||
else:
|
||||
# dump all displays
|
||||
depth = 0
|
||||
for disp in curr_inst().displays():
|
||||
print(f"Display {hex(disp)}")
|
||||
for screen in disp.screens:
|
||||
print(f'{" " * (depth + 1)}Screen@{hex(screen)}')
|
||||
self.dump_obj(screen, depth=depth + 1, limit=args.level)
|
||||
@@ -0,0 +1,75 @@
|
||||
import argparse
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
class Debugger(gdb.Command):
|
||||
"""Start debugpy server or connect to a debug server, so we can debug python code from IDE like PyCharm/VSCode"""
|
||||
|
||||
def __init__(self, host="localhost", port=11451):
|
||||
self.__host = host
|
||||
self.__port = port
|
||||
super().__init__("debugger", gdb.COMMAND_USER)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description=Debugger.__doc__)
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
type=str,
|
||||
help="Server listening host",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--port",
|
||||
type=int,
|
||||
help="Server listening port",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--type",
|
||||
choices=["pycharm", "vscode", "eclipse"],
|
||||
help="Debugger type",
|
||||
required=True,
|
||||
)
|
||||
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
if args.host:
|
||||
self.__host = args.host
|
||||
|
||||
if args.port:
|
||||
self.__port = args.port
|
||||
|
||||
if args.type == "pycharm":
|
||||
self.connect_to_pycharm()
|
||||
elif args.type == "vscode":
|
||||
self.connect_to_vscode()
|
||||
elif args.type == "eclipse":
|
||||
self.connect_to_eclipse()
|
||||
|
||||
def connect_to_pycharm(self):
|
||||
try:
|
||||
import pydevd_pycharm
|
||||
except ImportError:
|
||||
print("pydevd_pycharm module not found. Please install it using pip.")
|
||||
return
|
||||
|
||||
pydevd_pycharm.settrace(
|
||||
self.__host, port=self.__port, stdoutToServer=True, stderrToServer=True
|
||||
)
|
||||
|
||||
def connect_to_vscode(self):
|
||||
try:
|
||||
import debugpy
|
||||
except ImportError:
|
||||
print("debugpy module not found. Please install it using pip.")
|
||||
return
|
||||
|
||||
debugpy.listen((self.__host, self.__port))
|
||||
debugpy.wait_for_client()
|
||||
|
||||
def connect_to_eclipse(self):
|
||||
print("Eclipse is not implemented yet")
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lv_display import DumpDisplayBuf
|
||||
|
||||
__all__ = [
|
||||
"DumpDisplayBuf",
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
import argparse
|
||||
import gdb
|
||||
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
from lvglgdb.lvgl import LVDrawBuf
|
||||
|
||||
|
||||
class DumpDisplayBuf(gdb.Command):
|
||||
"""dump display buf to image"""
|
||||
|
||||
def __init__(self):
|
||||
super(DumpDisplayBuf, self).__init__(
|
||||
"dump display", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description="Dump display draw buffer.")
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--prefix",
|
||||
type=str,
|
||||
default="",
|
||||
help="prefix of dump file path",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--format",
|
||||
type=str,
|
||||
choices=["bmp", "png"],
|
||||
default=None,
|
||||
help="dump file format (bmp or png)",
|
||||
)
|
||||
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
display = curr_inst().disp_default()
|
||||
if not display:
|
||||
print("Error: Invalid display pointer")
|
||||
return
|
||||
buffers = {
|
||||
"buf_1": display.buf_1,
|
||||
"buf_2": display.buf_2,
|
||||
}
|
||||
|
||||
for buf_name, buf_ptr in buffers.items():
|
||||
if buf_ptr is not None:
|
||||
draw_buf = LVDrawBuf(buf_ptr)
|
||||
filename = f"{args.prefix}{buf_name}.{args.format.lower() if args.format else 'bmp'}"
|
||||
draw_buf.data_dump(filename, args.format)
|
||||
else:
|
||||
print(f"Warning: {buf_name} buffer is None, skipping.")
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lv_draw import InfoDrawUnit
|
||||
|
||||
__all__ = [
|
||||
"InfoDrawUnit",
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
|
||||
|
||||
class InfoDrawUnit(gdb.Command):
|
||||
"""dump draw unit info"""
|
||||
|
||||
def __init__(self):
|
||||
super(InfoDrawUnit, self).__init__(
|
||||
"info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def dump_draw_unit(self, draw_unit: Value):
|
||||
# Dereference to get the string content of the name from draw_unit
|
||||
name = draw_unit.name.string()
|
||||
|
||||
# Print draw_unit information and the name
|
||||
print(f"Draw Unit: {draw_unit}, Name: {name}")
|
||||
|
||||
# Handle different draw_units based on the name
|
||||
def lookup_type(name):
|
||||
try:
|
||||
return gdb.lookup_type(name)
|
||||
except gdb.error:
|
||||
return None
|
||||
|
||||
types = {
|
||||
"DMA2D": lookup_type("lv_draw_dma2d_unit_t"),
|
||||
"NEMA_GFX": lookup_type("lv_draw_nema_gfx_unit_t"),
|
||||
"NXP_PXP": lookup_type("lv_draw_pxp_unit_t"),
|
||||
"NXP_VGLITE": lookup_type("lv_draw_vglite_unit_t"),
|
||||
"OPENGLES": lookup_type("lv_draw_opengles_unit_t"),
|
||||
"DAVE2D": lookup_type("lv_draw_dave2d_unit_t"),
|
||||
"SDL": lookup_type("lv_draw_sdl_unit_t"),
|
||||
"SW": lookup_type("lv_draw_sw_unit_t"),
|
||||
"VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"),
|
||||
}
|
||||
|
||||
type = types.get(name, lookup_type("lv_draw_unit_t"))
|
||||
print(
|
||||
draw_unit.cast(type, ptr=True)
|
||||
.dereference()
|
||||
.format_string(pretty_structs=True, symbols=True)
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
for unit in curr_inst().draw_units():
|
||||
self.dump_draw_unit(unit)
|
||||
@@ -0,0 +1,5 @@
|
||||
from .nuttx import Lvglobal
|
||||
|
||||
__all__ = [
|
||||
"Lvglobal",
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
from .nuttx import Lvglobal
|
||||
|
||||
__all__ = [
|
||||
"Lvglobal",
|
||||
]
|
||||
@@ -0,0 +1,55 @@
|
||||
# GDB script to get lvgl global pointer in NuttX.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
class Lvglobal(gdb.Command):
|
||||
"""Set which lvgl instance to inspect by finding lv_global pointer in task TLS data."""
|
||||
|
||||
def __init__(self):
|
||||
super(Lvglobal, self).__init__("lvglobal", gdb.COMMAND_USER)
|
||||
|
||||
def set_lvgl_instance(self, inst):
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
|
||||
curr_inst().reset()
|
||||
curr_inst().ensure_init(inst)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
parser = argparse.ArgumentParser(description=self.__doc__)
|
||||
parser.add_argument("-p", "--pid", type=int, help="Optional process ID")
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(arg))
|
||||
except SystemExit:
|
||||
return
|
||||
try:
|
||||
from nxgdb import utils
|
||||
except ImportError:
|
||||
print("nxgdb is not installed, can't find lvgl global pointer.")
|
||||
return
|
||||
|
||||
lv_global = utils.gdb_eval_or_none("lv_global")
|
||||
if lv_global:
|
||||
print(f"Found single instance lv_global@{hex(lv_global.address)}")
|
||||
self.set_lvgl_instance(lv_global)
|
||||
return
|
||||
|
||||
# find the lvgl global pointer in tls
|
||||
if not args.pid:
|
||||
print("LVGL is in multi-process mode; please provide --pid.")
|
||||
return
|
||||
lv_key = utils.gdb_eval_or_none("lv_nuttx_tlskey")
|
||||
if lv_key is None:
|
||||
lv_key = utils.gdb_eval_or_none("lv_global_default::index")
|
||||
if lv_key is None:
|
||||
print("Can't find lvgl tls key in multi-process mode.")
|
||||
return
|
||||
lv_global = utils.get_task_tls(args.pid, lv_key)
|
||||
if lv_global:
|
||||
print(f"Found lv_global@{hex(lv_global)}")
|
||||
self.set_lvgl_instance(lv_global)
|
||||
else:
|
||||
print(f"\nCan't find lv_global with tlskey@{hex(lv_key)}.")
|
||||
@@ -0,0 +1,4 @@
|
||||
from .lv_style import InfoStyle
|
||||
from .lv_cache import DumpCache
|
||||
|
||||
__all__ = ["InfoStyle", "DumpCache"]
|
||||
@@ -0,0 +1,40 @@
|
||||
import argparse
|
||||
import gdb
|
||||
|
||||
|
||||
class DumpCache(gdb.Command):
|
||||
"""dump cache info for specified cache"""
|
||||
|
||||
def __init__(self):
|
||||
super(DumpCache, self).__init__(
|
||||
"dump cache", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description="Dump lvgl cache info.")
|
||||
parser.add_argument(
|
||||
"cache",
|
||||
type=str,
|
||||
choices=["image", "image_header"],
|
||||
default="image",
|
||||
help="cache to dump.",
|
||||
)
|
||||
|
||||
from lvglgdb import curr_inst
|
||||
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
cache = None
|
||||
if args.cache == "image":
|
||||
cache = curr_inst().image_cache()
|
||||
elif args.cache == "image_header":
|
||||
cache = curr_inst().image_header_cache()
|
||||
|
||||
if not cache:
|
||||
print("Invalid cache: ", args.cache)
|
||||
return
|
||||
|
||||
cache.print_entries()
|
||||
@@ -0,0 +1,40 @@
|
||||
import argparse
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from lvglgdb.lvgl import LVObject
|
||||
from lvglgdb.lvgl import dump_style_info
|
||||
|
||||
|
||||
class InfoStyle(gdb.Command):
|
||||
"""dump obj style value for specified obj"""
|
||||
|
||||
def __init__(self):
|
||||
super(InfoStyle, self).__init__(
|
||||
"info style", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description="Dump lvgl obj local style.")
|
||||
parser.add_argument(
|
||||
"obj",
|
||||
type=str,
|
||||
help="obj to show style.",
|
||||
)
|
||||
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
obj = gdb.parse_and_eval(args.obj)
|
||||
if not obj:
|
||||
print("Invalid obj: ", args.obj)
|
||||
return
|
||||
|
||||
obj = Value(obj)
|
||||
|
||||
# show all styles applied to this obj
|
||||
for style in LVObject(obj).styles:
|
||||
print(" ", end="")
|
||||
dump_style_info(style)
|
||||
@@ -0,0 +1,43 @@
|
||||
from .core import LVObject, curr_inst, dump_obj_info
|
||||
from .display import LVDisplay
|
||||
from .draw import LVDrawBuf
|
||||
from .misc import (
|
||||
LVList,
|
||||
dump_style_info,
|
||||
LVRedBlackTree,
|
||||
dump_rb_info,
|
||||
LVCache,
|
||||
dump_cache_info,
|
||||
LVCacheEntry,
|
||||
dump_cache_entry_info,
|
||||
LVCacheLRURB,
|
||||
dump_lru_rb_cache_info,
|
||||
LVCacheLRURBIterator,
|
||||
LVCacheIteratorBase,
|
||||
LVImageCache,
|
||||
LVImageHeaderCache,
|
||||
create_cache_iterator,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"LVObject",
|
||||
"LVDisplay",
|
||||
"LVDrawBuf",
|
||||
"curr_inst",
|
||||
"LVList",
|
||||
"dump_style_info",
|
||||
"dump_obj_info",
|
||||
"LVRedBlackTree",
|
||||
"dump_rb_info",
|
||||
"LVCache",
|
||||
"dump_cache_info",
|
||||
"LVCacheEntry",
|
||||
"dump_cache_entry_info",
|
||||
"LVCacheLRURB",
|
||||
"dump_lru_rb_cache_info",
|
||||
"LVCacheLRURBIterator",
|
||||
"LVCacheIteratorBase",
|
||||
"LVImageCache",
|
||||
"LVImageHeaderCache",
|
||||
"create_cache_iterator",
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
from .lv_obj import LVObject, dump_obj_info
|
||||
from .lv_global import curr_inst
|
||||
|
||||
__all__ = [
|
||||
"LVObject",
|
||||
"curr_inst",
|
||||
"dump_obj_info",
|
||||
]
|
||||
@@ -0,0 +1,117 @@
|
||||
import gdb
|
||||
from typing import Optional, Union, Iterator, TYPE_CHECKING
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from ..misc.lv_ll import LVList
|
||||
|
||||
"""
|
||||
LVGL global instance
|
||||
|
||||
Note:
|
||||
lv_global is the global instance of LVGL.
|
||||
It will contain other components like display, draw unit, etc.
|
||||
So if you want add a new component, please add it in this class.
|
||||
And just import your component when you need it. Not expose to the whole file.
|
||||
"""
|
||||
|
||||
__all__ = ["curr_inst"]
|
||||
|
||||
"""
|
||||
Type hint for IDE, just imported at typechecking phase not in runtime.
|
||||
"""
|
||||
if TYPE_CHECKING:
|
||||
from ..display.lv_display import LVDisplay
|
||||
|
||||
|
||||
class LVGL:
|
||||
"""LVGL instance"""
|
||||
|
||||
def __init__(self, lv_global: Value):
|
||||
self.lv_global = lv_global.cast("lv_global_t", ptr=True)
|
||||
|
||||
def displays(self) -> "Iterator[LVDisplay]":
|
||||
ll = self.lv_global.disp_ll
|
||||
if not ll:
|
||||
return
|
||||
|
||||
from ..display.lv_display import LVDisplay
|
||||
|
||||
for disp in LVList(ll, "lv_display_t"):
|
||||
yield LVDisplay(disp)
|
||||
|
||||
def disp_default(self):
|
||||
from ..display.lv_display import LVDisplay
|
||||
|
||||
disp_default = self.lv_global.disp_default
|
||||
return LVDisplay(disp_default) if disp_default else None
|
||||
|
||||
def screen_active(self):
|
||||
disp = self.lv_global.disp_default
|
||||
return disp.act_scr if disp else None
|
||||
|
||||
def draw_units(self):
|
||||
unit = self.lv_global.draw_info.unit_head
|
||||
|
||||
# Iterate through all draw units
|
||||
while unit:
|
||||
yield unit
|
||||
unit = unit.next
|
||||
|
||||
def image_cache(self):
|
||||
from ..misc.lv_image_cache import LVImageCache
|
||||
|
||||
return LVImageCache(self.lv_global.img_cache)
|
||||
|
||||
def image_header_cache(self):
|
||||
from ..misc.lv_image_header_cache import LVImageHeaderCache
|
||||
|
||||
return LVImageHeaderCache(self.lv_global.img_header_cache)
|
||||
|
||||
|
||||
class _LVGLSingleton:
|
||||
__slots__ = ("_lvgl", "_ready")
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._lvgl: Optional[object] = None
|
||||
self._ready = False
|
||||
|
||||
def ensure_init(self, lv_global: Union[gdb.Value, Value, None] = None) -> bool:
|
||||
if self._ready:
|
||||
return True
|
||||
|
||||
if lv_global is None:
|
||||
try:
|
||||
lv_global = Value(gdb.parse_and_eval("lv_global").address)
|
||||
except gdb.error as e:
|
||||
print(f"Failed to get lv_global: {e}")
|
||||
return False
|
||||
elif not isinstance(lv_global, Value):
|
||||
lv_global = Value(lv_global).cast("lv_global_t", ptr=True)
|
||||
|
||||
if not lv_global.inited:
|
||||
print(
|
||||
"\x1b[31mlvgl is not initialized yet. "
|
||||
"Please call `ensure_init()` later.\x1b[0m"
|
||||
)
|
||||
return False
|
||||
|
||||
self._lvgl = LVGL(lv_global)
|
||||
self._ready = True
|
||||
return True
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
if not self._ready and not self.ensure_init():
|
||||
raise RuntimeError("LVGL singleton not ready")
|
||||
return getattr(self._lvgl, name)
|
||||
|
||||
def reset(self) -> None:
|
||||
self._lvgl = None
|
||||
self._ready = False
|
||||
|
||||
|
||||
__curr_inst = _LVGLSingleton()
|
||||
|
||||
|
||||
def curr_inst() -> _LVGLSingleton:
|
||||
"""Get the global instance of LVGL"""
|
||||
return __curr_inst
|
||||
@@ -0,0 +1,74 @@
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVObject(Value):
|
||||
"""LVGL object"""
|
||||
|
||||
def __init__(self, obj: Value):
|
||||
super().__init__(obj.cast("lv_obj_t", ptr=True))
|
||||
|
||||
@property
|
||||
def class_name(self):
|
||||
name = self.class_p.name
|
||||
if name:
|
||||
return name.string()
|
||||
return self.class_p.format_string(symbols=True, address=True, styling=True)
|
||||
|
||||
@property
|
||||
def x1(self):
|
||||
return int(self.coords.x1)
|
||||
|
||||
@property
|
||||
def y1(self):
|
||||
return int(self.coords.y1)
|
||||
|
||||
@property
|
||||
def x2(self):
|
||||
return int(self.coords.x2)
|
||||
|
||||
@property
|
||||
def y2(self):
|
||||
return int(self.coords.y2)
|
||||
|
||||
@property
|
||||
def child_count(self):
|
||||
return self.spec_attr.child_cnt if self.spec_attr else 0
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
if not self.spec_attr:
|
||||
return
|
||||
|
||||
for i in range(self.child_count):
|
||||
child = self.spec_attr.children[i]
|
||||
yield LVObject(child)
|
||||
|
||||
@property
|
||||
def styles(self):
|
||||
LV_STYLE_PROP_INV = 0
|
||||
LV_STYLE_PROP_ANY = 0xFF
|
||||
count = self.style_cnt
|
||||
if count == 0:
|
||||
return
|
||||
|
||||
styles = self.super_value("styles")
|
||||
for i in range(count):
|
||||
style = styles[i].style
|
||||
prop_cnt = style.prop_cnt
|
||||
values_and_props = style.values_and_props.cast(
|
||||
"lv_style_const_prop_t", ptr=True
|
||||
)
|
||||
for j in range(prop_cnt):
|
||||
prop = values_and_props[j].prop
|
||||
if prop == LV_STYLE_PROP_INV or prop == LV_STYLE_PROP_ANY:
|
||||
continue
|
||||
yield values_and_props[j]
|
||||
|
||||
def get_child(self, index: int):
|
||||
return self.spec_attr.children[index] if self.spec_attr else None
|
||||
|
||||
|
||||
def dump_obj_info(obj: LVObject):
|
||||
clzname = obj.class_name
|
||||
coords = f"{obj.x1},{obj.y1},{obj.x2},{obj.y2}"
|
||||
print(f"{clzname}@{hex(obj)} {coords}")
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lv_display import LVDisplay
|
||||
|
||||
__all__ = [
|
||||
"LVDisplay",
|
||||
]
|
||||
@@ -0,0 +1,45 @@
|
||||
from ..core.lv_obj import LVObject
|
||||
from ..draw.lv_draw_buf import LVDrawBuf
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVDisplay(Value):
|
||||
"""LVGL display"""
|
||||
|
||||
def __init__(self, disp: Value):
|
||||
super().__init__(disp)
|
||||
|
||||
@property
|
||||
def hor_res(self) -> int:
|
||||
"""Get horizontal resolution in pixels"""
|
||||
return int(self.super_value("hor_res"))
|
||||
|
||||
@property
|
||||
def ver_res(self) -> int:
|
||||
"""Get vertical resolution in pixels"""
|
||||
return int(self.super_value("ver_res"))
|
||||
|
||||
@property
|
||||
def screens(self):
|
||||
screens = self.super_value("screens")
|
||||
for i in range(self.screen_cnt):
|
||||
yield LVObject(screens[i])
|
||||
|
||||
# Buffer-related properties
|
||||
@property
|
||||
def buf_1(self):
|
||||
"""Get first draw buffer (may be None)"""
|
||||
buf_ptr = self.super_value("buf_1")
|
||||
return LVDrawBuf(buf_ptr) if buf_ptr else None
|
||||
|
||||
@property
|
||||
def buf_2(self):
|
||||
"""Get second draw buffer (may be None)"""
|
||||
buf_ptr = self.super_value("buf_2")
|
||||
return LVDrawBuf(buf_ptr) if buf_ptr else None
|
||||
|
||||
@property
|
||||
def buf_act(self):
|
||||
"""Get currently active draw buffer (may be None)"""
|
||||
buf_ptr = self.super_value("buf_act")
|
||||
return LVDrawBuf(buf_ptr) if buf_ptr else None
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lv_draw_buf import LVDrawBuf
|
||||
|
||||
__all__ = [
|
||||
"LVDrawBuf",
|
||||
]
|
||||
@@ -0,0 +1,205 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import gdb
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVDrawBuf(Value):
|
||||
"""LVGL draw buffer"""
|
||||
|
||||
_color_formats = {
|
||||
"RGB565": None,
|
||||
"RGB888": None,
|
||||
"ARGB8888": None,
|
||||
"XRGB8888": None,
|
||||
}
|
||||
|
||||
_bpp = {
|
||||
"RGB565": 16,
|
||||
"RGB888": 24,
|
||||
"ARGB8888": 32,
|
||||
"XRGB8888": 32,
|
||||
}
|
||||
|
||||
def __init__(self, draw_buf: Value):
|
||||
super().__init__(draw_buf)
|
||||
self._init_color_formats()
|
||||
|
||||
def _init_color_formats(self):
|
||||
"""init color formats from enum"""
|
||||
for fmt in self._color_formats.keys():
|
||||
if self._color_formats[fmt] is None:
|
||||
try:
|
||||
self._color_formats[fmt] = int(
|
||||
gdb.parse_and_eval(f"LV_COLOR_FORMAT_{fmt}")
|
||||
)
|
||||
except gdb.error:
|
||||
print(f"Warning: Failed to get LV_COLOR_FORMAT_{fmt}")
|
||||
self._color_formats[fmt] = -1
|
||||
|
||||
def color_format_info(self) -> dict:
|
||||
"""get color format info"""
|
||||
header = self.super_value("header")
|
||||
cf = int(header["cf"])
|
||||
|
||||
fmt_name = "UNKNOWN"
|
||||
for name, val in self._color_formats.items():
|
||||
if val == cf:
|
||||
fmt_name = name
|
||||
break
|
||||
|
||||
return {"value": cf, "name": fmt_name, "bpp": self._bpp.get(fmt_name, 0)}
|
||||
|
||||
@property
|
||||
def header(self) -> dict:
|
||||
"""Get the image header information as a dictionary"""
|
||||
header = self.super_value("header")
|
||||
return {
|
||||
"w": header["w"],
|
||||
"h": header["h"],
|
||||
"stride": header["stride"],
|
||||
"cf": header["cf"], # Color format
|
||||
}
|
||||
|
||||
@property
|
||||
def data_size(self) -> int:
|
||||
"""Get the total buffer size in bytes"""
|
||||
return self.super_value("data_size")
|
||||
|
||||
@property
|
||||
def data(self) -> bytes:
|
||||
"""Get the buffer data ptr"""
|
||||
return self.super_value("data")
|
||||
|
||||
def data_dump(self, filename: str, format: str = None) -> bool:
|
||||
"""
|
||||
Dump the buffer data to an image file.
|
||||
|
||||
Args:
|
||||
filename: Output file path
|
||||
format: Image format (None for auto-detection from filename)
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Validate input parameters
|
||||
if not filename:
|
||||
raise ValueError("Output filename cannot be empty")
|
||||
|
||||
# Get buffer metadata
|
||||
header = self.super_value("header")
|
||||
stride = int(header["stride"])
|
||||
height = int(header["h"])
|
||||
cf_info = self.color_format_info()
|
||||
data_ptr = self.super_value("data")
|
||||
data_size = int(self.super_value("data_size"))
|
||||
width = (stride * 8) // cf_info["bpp"] if cf_info["bpp"] else 0
|
||||
expected_data_size = stride * height
|
||||
|
||||
# Validate buffer data
|
||||
if not data_ptr:
|
||||
raise ValueError("Data pointer is NULL")
|
||||
if width <= 0 or height <= 0:
|
||||
raise ValueError(f"Invalid dimensions: {width}x{height}")
|
||||
if data_size <= 0:
|
||||
raise ValueError(f"Invalid data size: {data_size}")
|
||||
if data_size < expected_data_size:
|
||||
raise ValueError(
|
||||
f"Data size mismatch: expected {expected_data_size}, got {data_size}"
|
||||
)
|
||||
elif data_size > expected_data_size:
|
||||
gdb.write(
|
||||
f"\033[93mData size mismatch: expected {expected_data_size}, got {data_size}\033[0m\n"
|
||||
)
|
||||
|
||||
# Read pixel data
|
||||
pixel_data = (
|
||||
gdb.selected_inferior()
|
||||
.read_memory(int(data_ptr), expected_data_size)
|
||||
.tobytes()
|
||||
)
|
||||
if not pixel_data:
|
||||
raise ValueError("Failed to read pixel data")
|
||||
|
||||
# Process based on color format
|
||||
img = self._convert_to_image(pixel_data, width, height, cf_info["value"])
|
||||
if img is None:
|
||||
return False
|
||||
|
||||
# Determine output format
|
||||
output_format = (
|
||||
format.upper() if format else Path(filename).suffix[1:].upper() or "BMP"
|
||||
)
|
||||
|
||||
# Save image
|
||||
img.save(filename, format=output_format)
|
||||
print(
|
||||
f"Successfully saved {cf_info['name']} buffer as {output_format} to {filename}"
|
||||
)
|
||||
return True
|
||||
|
||||
except gdb.MemoryError:
|
||||
print("Error: Failed to access memory")
|
||||
return False
|
||||
except ValueError as e:
|
||||
print(f"Validation error: {str(e)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
return False
|
||||
|
||||
def _convert_to_image(
|
||||
self, pixel_data: bytes, width: int, height: int, color_format: int
|
||||
) -> Optional[Image.Image]:
|
||||
"""
|
||||
Convert raw pixel data to PIL Image based on color format.
|
||||
|
||||
Args:
|
||||
pixel_data: Raw pixel bytes
|
||||
width: Image width
|
||||
height: Image height
|
||||
color_format: LVGL color format value
|
||||
|
||||
Returns:
|
||||
PIL.Image or None if conversion fails
|
||||
"""
|
||||
try:
|
||||
if color_format == self._color_formats["RGB565"]:
|
||||
# Convert RGB565 to RGB888
|
||||
arr = np.frombuffer(pixel_data, dtype=np.uint8)
|
||||
arr = arr.reshape((height, width, 2))
|
||||
rgb565 = np.frombuffer(pixel_data, dtype=np.uint16).reshape(
|
||||
(height, width)
|
||||
)
|
||||
r = (((rgb565 & 0xF800) >> 11) << 3).astype(np.uint8)
|
||||
g = ((rgb565 & 0x07E0) >> 3).astype(np.uint8)
|
||||
b = ((rgb565 & 0x001F) << 3).astype(np.uint8)
|
||||
return Image.fromarray(np.dstack((r, g, b)), "RGB")
|
||||
|
||||
elif color_format == self._color_formats["RGB888"]:
|
||||
arr = np.frombuffer(pixel_data, dtype=np.uint8).reshape(
|
||||
height, width, 3
|
||||
)
|
||||
rgb_arr = arr[:, :, [2, 1, 0]] # BGR -> RGB
|
||||
return Image.fromarray(rgb_arr, "RGB")
|
||||
|
||||
elif color_format == self._color_formats["ARGB8888"]:
|
||||
arr = np.frombuffer(pixel_data, dtype=np.uint8).reshape(-1, 4)
|
||||
rgba = np.column_stack((arr[:, 1:4], arr[:, 0])) # ARGB -> RGBA
|
||||
return Image.frombytes("RGBA", (width, height), rgba.tobytes())
|
||||
|
||||
elif color_format == self._color_formats["XRGB8888"]:
|
||||
arr = np.frombuffer(pixel_data, dtype=np.uint8).reshape(-1, 4)
|
||||
rgb = arr[:, [2, 1, 0]] # BGR -> RGB
|
||||
return Image.fromarray(rgb.reshape(height, width, 3), "RGB")
|
||||
|
||||
raise ValueError(f"Unsupported color format: 0x{color_format:x}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Conversion error: {str(e)}")
|
||||
return None
|
||||
@@ -0,0 +1,194 @@
|
||||
# LVGL GDB Scripts - Misc Module Extensions
|
||||
|
||||
This directory contains GDB debugging scripts for LVGL's miscellaneous data structures, including red-black trees and
|
||||
cache systems.
|
||||
|
||||
## Objects
|
||||
|
||||
### 1. lv_rb.py - Red-Black Tree Support
|
||||
|
||||
Provides debugging support for LVGL's red-black tree (`lv_rb_t`) implementation.
|
||||
|
||||
**Classes:**
|
||||
|
||||
- `LVRedBlackTree` - Iterator for red-black tree nodes
|
||||
|
||||
**Functions:**
|
||||
|
||||
- `dump_rb_info(rb, datatype)` - Dump basic red-black tree information
|
||||
|
||||
**Usage:**
|
||||
|
||||
```gdbscript
|
||||
# In GDB:
|
||||
python tree = LVRedBlackTree(some_rb_tree)
|
||||
python for data in tree: print(data)
|
||||
python dump_rb_info(some_rb_tree, datatype)
|
||||
```
|
||||
|
||||
### 2. lv_cache.py - Cache System Support
|
||||
|
||||
Provides debugging support for LVGL's cache system (`lv_cache_t`).
|
||||
|
||||
**Classes:**
|
||||
|
||||
- `LVCache` - Wrapper for cache objects
|
||||
|
||||
**Functions:**
|
||||
|
||||
- `dump_cache_info(cache, datatype)` - Dump cache information
|
||||
|
||||
**Usage:**
|
||||
|
||||
```gdbscript
|
||||
# In GDB:
|
||||
python cache = LVCache(some_cache)
|
||||
python print(f"Cache usage: {cache.get_usage_percentage()}%")
|
||||
python dump_cache_info(some_cache, datatype)
|
||||
```
|
||||
|
||||
### 3. lv_cache_entry.py - Cache Entry Support
|
||||
|
||||
Provides debugging support for cache entries (`lv_cache_entry_t`).
|
||||
|
||||
**Classes:**
|
||||
|
||||
- `LVCacheEntry` - Wrapper for cache entries
|
||||
|
||||
**Functions:**
|
||||
|
||||
- `dump_cache_entry_info(entry, datatype)` - Dump cache entry information
|
||||
|
||||
**Usage:**
|
||||
|
||||
```gdbscript
|
||||
# In GDB:
|
||||
python entry = LVCacheEntry(some_entry)
|
||||
python print(f"Ref count: {entry.get_ref_count()}")
|
||||
python dump_cache_entry_info(some_entry, datatype)
|
||||
```
|
||||
|
||||
### 4. lv_cache_lru_rb.py - LRU Cache with RB Tree Support
|
||||
|
||||
Specialized support for LRU-based caches using red-black trees.
|
||||
|
||||
**Classes:**
|
||||
|
||||
- `LVCacheLRURB` - Specialized cache wrapper for LRU RB implementations
|
||||
|
||||
**Functions:**
|
||||
|
||||
- `dump_lru_rb_cache_info(cache)` - Dump LRU RB cache information
|
||||
|
||||
### 5. lv_image_cache.py and lv_image_header_cache.py - Image Cache Support
|
||||
|
||||
Provides debugging support for LVGL's image cache (`lv_image_cache_t`).
|
||||
|
||||
**Classes:**
|
||||
|
||||
- `LVImageCache` - Wrapper for image cache objects
|
||||
|
||||
**Usage:**
|
||||
|
||||
```gdbscript
|
||||
# In GDB:
|
||||
python curr_inst().image_cache().print_info()
|
||||
python curr_inst().image_cache().print_entries()
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
All these modules are automatically imported and available when using the LVGL GDB scripts. You can access them through
|
||||
the main `lvgl` module:
|
||||
|
||||
```gdbscript
|
||||
# In GDB:
|
||||
python from lvglgdb import LVRedBlackTree, LVCache, dump_rb_info, dump_cache_info
|
||||
```
|
||||
|
||||
## Type Conversion Support
|
||||
|
||||
All wrapper classes support multiple input types:
|
||||
|
||||
- `gdb.Value` objects (from GDB expressions)
|
||||
- `Value` objects (from lvglgdb.value module)
|
||||
- Raw pointers as integers (e.g., `0x12345678`)
|
||||
|
||||
This allows flexible usage patterns:
|
||||
|
||||
```gdbscript
|
||||
# From GDB variable
|
||||
entry = LVCacheEntry(my_entry)
|
||||
|
||||
# From raw memory address
|
||||
entry = LVCacheEntry(0x7fff12345678)
|
||||
|
||||
# From GDB expression result
|
||||
entry = LVCacheEntry(gdb.parse_and_eval("*(lv_cache_entry_t*)0x7fff12345678"))
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Debugging a Red-Black Tree
|
||||
|
||||
```gdbscript
|
||||
# Create tree iterator from variable
|
||||
tree = LVRedBlackTree(some_rb_variable)
|
||||
|
||||
# Create from raw pointer (0x12345678)
|
||||
tree = LVRedBlackTree(0x12345678)
|
||||
|
||||
# Iterate through all nodes
|
||||
for data_ptr in tree:
|
||||
print(f"Data pointer: {data_ptr}")
|
||||
|
||||
# Get tree info
|
||||
print(f"Tree has {len(tree)} nodes")
|
||||
tree.print_info()
|
||||
```
|
||||
|
||||
### Debugging a Cache
|
||||
|
||||
```gdbscript
|
||||
# Create cache wrapper from variable
|
||||
cache = LVCache(some_cache_variable)
|
||||
|
||||
# Create from raw pointer (0x12345678)
|
||||
cache = LVCache(0x12345678)
|
||||
|
||||
# Check cache status
|
||||
print(f"Cache usage: {cache.get_usage_percentage():.1f}%")
|
||||
print(f"Cache enabled: {cache.is_enabled()}")
|
||||
|
||||
# Dump full info
|
||||
cache.print_info()
|
||||
```
|
||||
|
||||
### Debugging Cache Entries
|
||||
|
||||
```gdbscript
|
||||
# Create entry wrapper from variable
|
||||
entry = LVCacheEntry(some_entry_variable)
|
||||
|
||||
# Create from raw pointer (0x12345678)
|
||||
entry = LVCacheEntry(0x12345678)
|
||||
|
||||
# Check entry status
|
||||
print(f"Ref count: {entry.get_ref_count()}")
|
||||
print(f"Invalid: {entry.is_invalid()}")
|
||||
|
||||
# Dump full info
|
||||
entry.print_info()
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
- `lv_rb.py` - Red-black tree debugging support
|
||||
- `lv_cache.py` - General cache debugging support
|
||||
- `lv_cache_entry.py` - Cache entry debugging support
|
||||
- `lv_cache_lru_rb.py` - LRU RB cache specialized support
|
||||
- `lv_image_cache.py` - Image cache debugging support
|
||||
- `lv_image_header_cache.py` - Image header cache debugging support
|
||||
- `lv_cache_iter_base.py` - Cache iterator base class
|
||||
- `lv_cache_iter_factory.py` - Cache iterator factory functions
|
||||
- `__init__.py` - Module exports and initialization
|
||||
@@ -0,0 +1,28 @@
|
||||
from .lv_ll import LVList
|
||||
from .lv_style import dump_style_info
|
||||
from .lv_rb import LVRedBlackTree, dump_rb_info
|
||||
from .lv_cache import LVCache, dump_cache_info
|
||||
from .lv_cache_entry import LVCacheEntry, dump_cache_entry_info
|
||||
from .lv_cache_lru_rb import LVCacheLRURB, dump_lru_rb_cache_info, LVCacheLRURBIterator
|
||||
from .lv_cache_iter_base import LVCacheIteratorBase
|
||||
from .lv_cache_iter_factory import create_cache_iterator
|
||||
from .lv_image_cache import LVImageCache
|
||||
from .lv_image_header_cache import LVImageHeaderCache
|
||||
|
||||
__all__ = [
|
||||
"LVList",
|
||||
"dump_style_info",
|
||||
"LVRedBlackTree",
|
||||
"dump_rb_info",
|
||||
"LVCache",
|
||||
"dump_cache_info",
|
||||
"LVCacheEntry",
|
||||
"dump_cache_entry_info",
|
||||
"LVCacheLRURB",
|
||||
"dump_lru_rb_cache_info",
|
||||
"LVCacheIteratorBase",
|
||||
"LVCacheLRURBIterator",
|
||||
"LVImageCache",
|
||||
"LVImageHeaderCache",
|
||||
"create_cache_iterator",
|
||||
]
|
||||
@@ -0,0 +1,99 @@
|
||||
from typing import Union, List, Optional, Dict
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from .lv_cache_iter_factory import create_cache_iterator
|
||||
|
||||
|
||||
class LVCache(Value):
|
||||
"""LVGL cache wrapper - focuses on cache-level operations"""
|
||||
|
||||
def __init__(
|
||||
self, cache: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
|
||||
):
|
||||
# Convert to Value first if needed
|
||||
if isinstance(cache, int):
|
||||
cache = Value(cache).cast("lv_cache_t", ptr=True)
|
||||
if cache is None:
|
||||
raise ValueError("Failed to cast pointer to lv_cache_t")
|
||||
elif isinstance(cache, gdb.Value) and not isinstance(cache, Value):
|
||||
cache = Value(cache)
|
||||
elif not cache:
|
||||
raise ValueError("Invalid cache")
|
||||
|
||||
self.datatype = (
|
||||
gdb.lookup_type(datatype).pointer()
|
||||
if isinstance(datatype, str)
|
||||
else datatype
|
||||
)
|
||||
|
||||
super().__init__(cache)
|
||||
|
||||
def print_info(self):
|
||||
"""Dump cache information"""
|
||||
print(f"Cache Info:")
|
||||
print(f" Name: {self.name.as_string()}")
|
||||
print(f" Node Size: {int(self.node_size)}")
|
||||
print(f" Max Size: {int(self.max_size)}")
|
||||
print(f" Current Size: {int(self.size)}")
|
||||
print(f" Free Size: {int(self.max_size) - int(self.size)}")
|
||||
print(f" Enabled: {bool(int(self.max_size) > 0)}")
|
||||
|
||||
# Try to identify cache type
|
||||
try:
|
||||
iterator = create_cache_iterator(self)
|
||||
print(f" Iterator Type: {iterator.__class__.__name__}")
|
||||
iterator.cache.print_info()
|
||||
except gdb.error:
|
||||
pass
|
||||
|
||||
def is_enabled(self):
|
||||
"""Check if cache is enabled"""
|
||||
return int(self.max_size) > 0
|
||||
|
||||
def get_usage_percentage(self):
|
||||
"""Get cache usage percentage"""
|
||||
if int(self.max_size) == 0:
|
||||
return 0.0
|
||||
return int(self.size) / int(self.max_size) * 100.0
|
||||
|
||||
def __iter__(self):
|
||||
"""Create appropriate iterator based on cache class"""
|
||||
return create_cache_iterator(self)
|
||||
|
||||
def items(self):
|
||||
"""Get all cache entries as a list"""
|
||||
entries = []
|
||||
for entry in self:
|
||||
entries.append(entry)
|
||||
return entries
|
||||
|
||||
def print_entries(self, max_entries=10):
|
||||
"""Print cache entries in readable format"""
|
||||
cache_entries = self.items()
|
||||
cache_entries_cnt = len(cache_entries)
|
||||
print(f"Cache Entries ({cache_entries_cnt} total):")
|
||||
|
||||
count = 0
|
||||
for i, entry in enumerate(cache_entries):
|
||||
if count >= max_entries:
|
||||
print(
|
||||
f" ... showing first {max_entries} of {cache_entries_cnt} entries"
|
||||
)
|
||||
break
|
||||
|
||||
print(f" [{i}] {entry}")
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
print(" (empty)")
|
||||
elif count < int(cache_entries_cnt):
|
||||
print(f" ... {cache_entries_cnt - count} more entries not shown")
|
||||
|
||||
|
||||
def dump_cache_info(
|
||||
cache: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
|
||||
):
|
||||
"""Dump cache information"""
|
||||
cache_obj = LVCache(cache, datatype)
|
||||
cache_obj.print_info()
|
||||
@@ -0,0 +1,103 @@
|
||||
from typing import Union
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVCacheEntry(Value):
|
||||
"""LVGL cache entry wrapper - focuses on entry-level operations"""
|
||||
|
||||
def __init__(
|
||||
self, entry: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
|
||||
):
|
||||
# Convert to Value first if needed
|
||||
if isinstance(entry, int):
|
||||
entry = Value(entry).cast("lv_cache_entry_t", ptr=True)
|
||||
if entry is None:
|
||||
raise ValueError("Failed to cast pointer to lv_cache_entry_t")
|
||||
elif isinstance(entry, gdb.Value) and not isinstance(entry, Value):
|
||||
entry = Value(entry)
|
||||
elif not entry:
|
||||
raise ValueError("Invalid cache entry")
|
||||
|
||||
self.datatype = (
|
||||
gdb.lookup_type(datatype).pointer()
|
||||
if isinstance(datatype, str)
|
||||
else datatype
|
||||
)
|
||||
|
||||
super().__init__(entry)
|
||||
|
||||
@classmethod
|
||||
def from_data_ptr(
|
||||
cls, data_ptr: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
|
||||
):
|
||||
"""Create LVCacheEntry from data pointer"""
|
||||
|
||||
if data_ptr.type == gdb.lookup_type("void").pointer() and datatype is None:
|
||||
raise ValueError("Data pointer is void*, datatype must be provided")
|
||||
|
||||
data_ptr = data_ptr.cast(datatype)
|
||||
|
||||
entry_ptr = int(data_ptr) + data_ptr.type.target().sizeof
|
||||
return cls(entry_ptr, datatype)
|
||||
|
||||
def print_info(self):
|
||||
"""Dump cache entry information"""
|
||||
print(f"Cache Entry Info:")
|
||||
print(f" Reference Count: {int(self.ref_cnt)}")
|
||||
print(f" Node Size: {int(self.node_size)}")
|
||||
print(f" Flags: {int(self.flags)}")
|
||||
print(f" Invalid: {self.is_invalid()}")
|
||||
print(f" Disable Delete: {self.is_disabled_delete()}")
|
||||
|
||||
# Try to get cache info if available
|
||||
try:
|
||||
cache = self.cache
|
||||
if cache:
|
||||
print(f" Cache: {cache}")
|
||||
except:
|
||||
pass
|
||||
|
||||
def get_data(self):
|
||||
"""Get entry data pointer"""
|
||||
data_ptr = Value(int(self) - self.get_node_size()).cast(self.datatype)
|
||||
return data_ptr
|
||||
|
||||
def is_invalid(self):
|
||||
"""Check if entry is invalid"""
|
||||
return bool(int(self.flags) & 1) # LV_CACHE_ENTRY_FLAG_INVALID
|
||||
|
||||
def is_disabled_delete(self):
|
||||
"""Check if entry has disable delete flag"""
|
||||
return bool(int(self.flags) & 2) # LV_CACHE_ENTRY_FLAG_DISABLE_DELETE
|
||||
|
||||
def get_ref_count(self):
|
||||
"""Get reference count"""
|
||||
return int(self.ref_cnt)
|
||||
|
||||
def get_node_size(self):
|
||||
"""Get node size"""
|
||||
return int(self.node_size)
|
||||
|
||||
def get_flags(self):
|
||||
"""Get flags"""
|
||||
return int(self.flags)
|
||||
|
||||
def __str__(self):
|
||||
"""Provide better string representation for debugging"""
|
||||
try:
|
||||
data = self.get_data()
|
||||
return f"CacheEntry(ref_cnt={self.get_ref_count()}, valid={not self.is_invalid()}, data={data.dereference()})"
|
||||
except gdb.error:
|
||||
pass
|
||||
|
||||
return super().__str__()
|
||||
|
||||
|
||||
def dump_cache_entry_info(
|
||||
entry: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str]
|
||||
):
|
||||
"""Dump cache entry information"""
|
||||
entry_obj = LVCacheEntry(entry, datatype)
|
||||
entry_obj.print_info()
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import List, Optional
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVCacheIteratorBase:
|
||||
"""Base class for cache iterators"""
|
||||
|
||||
def __init__(self, cache):
|
||||
self.cache = cache
|
||||
self._entries: List[Value] = []
|
||||
self._current_index = 0
|
||||
self._collect_entries()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
"""Get next cache entry"""
|
||||
if self._current_index < len(self._entries):
|
||||
entry = self._entries[self._current_index]
|
||||
self._current_index += 1
|
||||
return entry
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
def __len__(self):
|
||||
return len(self._entries)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.__class__.__name__}(cache={self.cache.name}, entries={len(self._entries)})"
|
||||
|
||||
def _collect_entries(self):
|
||||
"""To be implemented by subclasses"""
|
||||
raise NotImplementedError("Subclasses must implement _collect_entries")
|
||||
@@ -0,0 +1,32 @@
|
||||
import gdb
|
||||
import re
|
||||
|
||||
|
||||
from .lv_cache_iter_base import LVCacheIteratorBase
|
||||
|
||||
|
||||
def create_cache_iterator(cache) -> LVCacheIteratorBase | None:
|
||||
"""Factory function to create appropriate iterator for cache class"""
|
||||
|
||||
from .lv_cache_lru_rb import LVCacheLRURB
|
||||
|
||||
def get_class(s):
|
||||
if s == "lv_cache_class_lru_rb_size":
|
||||
return LVCacheLRURB
|
||||
elif s == "lv_cache_class_lru_rb_count":
|
||||
return LVCacheLRURB
|
||||
return None
|
||||
|
||||
try:
|
||||
if cache.clz:
|
||||
symbol = cache.clz.format_string(symbols=True, address=False)
|
||||
m = re.match("<(.*)>", symbol)
|
||||
if m:
|
||||
symbol = m.group(1)
|
||||
|
||||
cache_class = get_class(symbol)
|
||||
if cache_class:
|
||||
return iter(cache_class(cache))
|
||||
|
||||
except Exception:
|
||||
raise TypeError(f"Unsupported cache type {cache}")
|
||||
@@ -0,0 +1,104 @@
|
||||
from typing import Union, List, Optional
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from .lv_cache_iter_base import LVCacheIteratorBase
|
||||
from .lv_rb import LVRedBlackTree
|
||||
from .lv_cache_entry import LVCacheEntry
|
||||
from .lv_cache import LVCache
|
||||
|
||||
|
||||
class LVCacheLRURBIterator(LVCacheIteratorBase):
|
||||
"""Iterator for LRU RB cache implementation - traverses linked list and red-black tree"""
|
||||
|
||||
def __init__(self, cache):
|
||||
super().__init__(cache)
|
||||
|
||||
def _collect_entries(self):
|
||||
"""Collect entries from LRU RB cache by traversing the linked list"""
|
||||
try:
|
||||
# Cast cache to lv_lru_rb_t_ to access internal structures
|
||||
lru_cache = self.cache.cast("lv_lru_rb_t_", ptr=True)
|
||||
if not lru_cache:
|
||||
return
|
||||
|
||||
# Access the linked list
|
||||
rb = lru_cache.rb
|
||||
if not rb or not rb.root:
|
||||
return
|
||||
|
||||
rb = LVRedBlackTree(rb)
|
||||
for node in rb:
|
||||
self._entries.append(
|
||||
LVCacheEntry.from_data_ptr(node, self.cache.datatype)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error in _collect_lru_entries: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
class LVCacheLRURB(LVCache):
|
||||
"""LVGL LRU-based cache using red-black tree iterator"""
|
||||
|
||||
def __init__(self, cache: Union[Value, gdb.Value, int]):
|
||||
# Convert to Value first if needed
|
||||
if isinstance(cache, int):
|
||||
cache = Value(cache).cast("lv_cache_lru_rb_t", ptr=True)
|
||||
if cache is None:
|
||||
raise ValueError("Failed to cast pointer to lv_cache_lru_rb_t")
|
||||
elif isinstance(cache, gdb.Value) and not isinstance(cache, Value):
|
||||
cache = Value(cache)
|
||||
elif not cache:
|
||||
raise ValueError("Invalid cache")
|
||||
self.cache_base = cache
|
||||
super().__init__(cache, cache.datatype)
|
||||
|
||||
def print_info(self):
|
||||
"""Dump LRU RB cache information"""
|
||||
print(f"LRU RB Cache Info:")
|
||||
|
||||
# Try to get cache class info
|
||||
try:
|
||||
clz = self.clz
|
||||
if clz:
|
||||
print(f" Cache Class: {clz}")
|
||||
# Check if it's LRU RB based
|
||||
if "lru_rb" in str(clz).lower():
|
||||
print(f" Type: LRU with Red-Black Tree")
|
||||
except:
|
||||
pass
|
||||
|
||||
def is_count_based(self):
|
||||
"""Check if this is count-based LRU cache"""
|
||||
try:
|
||||
name = str(self.name)
|
||||
return "count" in name.lower() or "lru_rb_count" in str(self.clz).lower()
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_size_based(self):
|
||||
"""Check if this is size-based LRU cache"""
|
||||
try:
|
||||
name = str(self.name)
|
||||
return "size" in name.lower() or "lru_rb_size" in str(self.clz).lower()
|
||||
except:
|
||||
return False
|
||||
|
||||
def __iter__(self):
|
||||
"""Create iterator for this LRU RB cache"""
|
||||
return LVCacheLRURBIterator(self)
|
||||
|
||||
def items(self):
|
||||
"""Get all cache entries as a list"""
|
||||
entries = []
|
||||
for entry in self:
|
||||
entries.append(entry)
|
||||
return entries
|
||||
|
||||
|
||||
def dump_lru_rb_cache_info(cache: Union[Value, gdb.Value, int]):
|
||||
"""Dump LRU RB cache information"""
|
||||
cache_obj = LVCacheLRURB(cache)
|
||||
cache_obj.print_info()
|
||||
@@ -0,0 +1,88 @@
|
||||
from typing import Union
|
||||
import gdb
|
||||
from prettytable import PrettyTable
|
||||
from lvglgdb.value import Value
|
||||
from .lv_cache import LVCache
|
||||
from .lv_cache_entry import LVCacheEntry
|
||||
|
||||
|
||||
class LVImageCache(object):
|
||||
def __init__(self, cache: Value):
|
||||
self._cache = LVCache(cache, "lv_image_cache_data_t")
|
||||
|
||||
def print_info(self):
|
||||
self._cache.print_info()
|
||||
|
||||
def print_entries(self):
|
||||
"""Print image cache entries using prettytable format"""
|
||||
table = PrettyTable()
|
||||
table.field_names = [
|
||||
"size",
|
||||
"data_size",
|
||||
"cf",
|
||||
"rc",
|
||||
"type",
|
||||
"decoder",
|
||||
"decoded",
|
||||
"src",
|
||||
]
|
||||
table.align = "r" # Right align all columns by default
|
||||
table.align["src"] = "l" # Left align source column
|
||||
table.align["type"] = "c" # Center align type column
|
||||
|
||||
for entry in self._cache:
|
||||
entry: LVCacheEntry
|
||||
|
||||
data_ptr = entry.get_data()
|
||||
if not data_ptr:
|
||||
continue
|
||||
|
||||
decoded = data_ptr.decoded
|
||||
try:
|
||||
header = decoded.header
|
||||
w = int(header.w)
|
||||
h = int(header.h)
|
||||
cf = int(header.cf)
|
||||
|
||||
data_size = int(decoded.data_size) if decoded else 0
|
||||
decoded_ptr = decoded.data if decoded else 0
|
||||
decoder_name = data_ptr.decoder.name.as_string()
|
||||
src_type = int(data_ptr.src_type)
|
||||
src = data_ptr.src
|
||||
|
||||
ref_cnt = entry.get_ref_count()
|
||||
|
||||
size_str = f"{w}x{h}"
|
||||
|
||||
if src_type == 0: # LV_IMAGE_SRC_VARIABLE
|
||||
src_str = src.format_string(
|
||||
symbols=True, address=True, styling=True
|
||||
)
|
||||
type_str = "var"
|
||||
elif src_type == 1: # LV_IMAGE_SRC_FILE
|
||||
src_str = (
|
||||
src.cast("char", ptr=True).as_string() if src else "(null)"
|
||||
)
|
||||
type_str = "file"
|
||||
else: # Unknown type
|
||||
src_str = f"{int(src):#x}" if src else "0x0"
|
||||
type_str = "unkn"
|
||||
|
||||
table.add_row(
|
||||
[
|
||||
size_str,
|
||||
f"{data_size}",
|
||||
f"{cf}",
|
||||
f"{ref_cnt}",
|
||||
type_str,
|
||||
decoder_name,
|
||||
f"{int(decoded_ptr):#x}",
|
||||
src_str,
|
||||
]
|
||||
)
|
||||
|
||||
except gdb.error as e:
|
||||
table.add_row(["ERROR", "", "", "", "", "", "", str(e)])
|
||||
continue
|
||||
|
||||
print(table)
|
||||
@@ -0,0 +1,74 @@
|
||||
from typing import Union
|
||||
import gdb
|
||||
from prettytable import PrettyTable
|
||||
from lvglgdb.value import Value
|
||||
from .lv_cache import LVCache
|
||||
from .lv_cache_entry import LVCacheEntry
|
||||
|
||||
|
||||
class LVImageHeaderCache(object):
|
||||
def __init__(self, cache: Value):
|
||||
self._cache = LVCache(cache, "lv_image_header_cache_data_t")
|
||||
|
||||
def print_info(self):
|
||||
self._cache.print_info()
|
||||
|
||||
def print_entries(self):
|
||||
"""Print image cache entries using prettytable format"""
|
||||
table = PrettyTable()
|
||||
table.field_names = ["size", "cf", "rc", "type", "decoder", "src"]
|
||||
table.align = "r" # Right align all columns by default
|
||||
table.align["src"] = "l" # Left align source column
|
||||
table.align["type"] = "c" # Center align type column
|
||||
|
||||
for entry in self._cache:
|
||||
entry: LVCacheEntry
|
||||
|
||||
data_ptr = entry.get_data()
|
||||
if not data_ptr:
|
||||
continue
|
||||
|
||||
try:
|
||||
header = data_ptr.header
|
||||
w = int(header.w)
|
||||
h = int(header.h)
|
||||
cf = int(header.cf)
|
||||
|
||||
src_type = int(data_ptr.src_type)
|
||||
src = data_ptr.src
|
||||
decoder_name = data_ptr.decoder.name.as_string()
|
||||
|
||||
ref_cnt = entry.get_ref_count()
|
||||
|
||||
size_str = f"{w}x{h}"
|
||||
|
||||
if src_type == 0: # LV_IMAGE_SRC_VARIABLE
|
||||
src_str = src.format_string(
|
||||
symbols=True, address=True, styling=True
|
||||
)
|
||||
type_str = "var"
|
||||
elif src_type == 1: # LV_IMAGE_SRC_FILE
|
||||
src_str = (
|
||||
src.cast("char", ptr=True).as_string() if src else "(null)"
|
||||
)
|
||||
type_str = "file"
|
||||
else: # Unknown type
|
||||
src_str = f"{int(src):#x}" if src else "0x0"
|
||||
type_str = "unkn"
|
||||
|
||||
table.add_row(
|
||||
[
|
||||
size_str,
|
||||
f"{cf}",
|
||||
f"{ref_cnt}",
|
||||
type_str,
|
||||
decoder_name,
|
||||
src_str,
|
||||
]
|
||||
)
|
||||
|
||||
except gdb.error as e:
|
||||
table.add_row(["ERROR", "", "", "", "", str(e)])
|
||||
continue
|
||||
|
||||
print(table)
|
||||
@@ -0,0 +1,53 @@
|
||||
from typing import Union
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVList(Value):
|
||||
"""LVGL linked list iterator"""
|
||||
|
||||
def __init__(self, ll: Value, nodetype: Union[gdb.Type, str] = None):
|
||||
if not ll:
|
||||
raise ValueError("Invalid linked list")
|
||||
super().__init__(ll)
|
||||
|
||||
self.nodetype = (
|
||||
gdb.lookup_type(nodetype).pointer()
|
||||
if isinstance(nodetype, str)
|
||||
else nodetype
|
||||
)
|
||||
self.lv_ll_node_t = gdb.lookup_type("lv_ll_node_t").pointer()
|
||||
self.current = self.head
|
||||
self._next_offset = self.n_size + self.lv_ll_node_t.sizeof
|
||||
self._prev_offset = self.n_size
|
||||
|
||||
def _next(self, node):
|
||||
next_value = Value(int(node) + self._next_offset)
|
||||
return next_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
||||
|
||||
def _prev(self, node):
|
||||
prev_value = Value(int(node) + self._prev_offset)
|
||||
return prev_value.cast(self.lv_ll_node_t, ptr=True).dereference()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if not self.current:
|
||||
raise StopIteration
|
||||
|
||||
nodetype = self.nodetype if self.nodetype else self.lv_ll_node_t
|
||||
node = self.current.cast(nodetype)
|
||||
|
||||
self.current = self._next(self.current)
|
||||
return node
|
||||
|
||||
@property
|
||||
def len(self):
|
||||
len = 0
|
||||
node = self.head
|
||||
while node:
|
||||
len += 1
|
||||
node = self._next(node)
|
||||
return len
|
||||
@@ -0,0 +1,183 @@
|
||||
from typing import Union
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
class LVRedBlackTree(Value):
|
||||
"""LVGL red-black tree iterator"""
|
||||
|
||||
def __init__(
|
||||
self, rb: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str] = None
|
||||
):
|
||||
# Convert to Value first if needed
|
||||
if isinstance(rb, int):
|
||||
rb = Value(rb).cast("lv_rb_t", ptr=True)
|
||||
if rb is None:
|
||||
raise ValueError("Failed to cast pointer to lv_rb_t")
|
||||
elif isinstance(rb, gdb.Value) and not isinstance(rb, Value):
|
||||
rb = Value(rb)
|
||||
elif not rb:
|
||||
raise ValueError("Invalid red-black tree")
|
||||
super().__init__(rb)
|
||||
|
||||
self.lv_rb_node_t = gdb.lookup_type("lv_rb_node_t").pointer()
|
||||
|
||||
self.datatype = (
|
||||
gdb.lookup_type(datatype).pointer()
|
||||
if isinstance(datatype, str)
|
||||
else datatype
|
||||
)
|
||||
|
||||
def minimum_from(self, node):
|
||||
"""Find minimum node from given node"""
|
||||
if not node:
|
||||
return None
|
||||
|
||||
current = node
|
||||
while current.left:
|
||||
current = current.left
|
||||
return current
|
||||
|
||||
def maximum_from(self, node):
|
||||
"""Find maximum node from given node"""
|
||||
if not node:
|
||||
return None
|
||||
|
||||
current = node
|
||||
while current.right:
|
||||
current = current.right
|
||||
return current
|
||||
|
||||
def minimum(self):
|
||||
"""Find minimum node in the tree"""
|
||||
root = self.root
|
||||
return self.minimum_from(root)
|
||||
|
||||
def maximum(self):
|
||||
"""Find maximum node in the tree"""
|
||||
root = self.root
|
||||
return self.maximum_from(root)
|
||||
|
||||
def __iter__(self):
|
||||
"""Create a new iterator for this tree"""
|
||||
return LVRedBlackTreeIterator(self)
|
||||
|
||||
def __len__(self):
|
||||
"""Get the number of nodes in the tree"""
|
||||
count = 0
|
||||
for _ in self:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def get_data(self, node):
|
||||
"""Get typed data from node, with optional type conversion"""
|
||||
if not node or not node.data:
|
||||
return None
|
||||
|
||||
data = node.data
|
||||
if self.datatype:
|
||||
return data.cast(self.datatype)
|
||||
return data
|
||||
|
||||
def format_data(self, data):
|
||||
"""Format data for display - simple GDB style"""
|
||||
if data is None:
|
||||
return "None"
|
||||
|
||||
try:
|
||||
ptr_addr = f"0x{int(data):x}"
|
||||
except:
|
||||
return str(data)
|
||||
|
||||
if self.datatype and data:
|
||||
try:
|
||||
struct_data = data.dereference()
|
||||
return f"{ptr_addr} -> {struct_data}"
|
||||
except:
|
||||
pass
|
||||
|
||||
return ptr_addr
|
||||
|
||||
def print_info(self):
|
||||
"""Dump basic tree information"""
|
||||
print(f"Red-Black Tree Info:")
|
||||
print(f" Size: {int(self.size)}")
|
||||
print(f" Node Count: {len(self)}")
|
||||
print(f" Root: {self.root}")
|
||||
if self.root:
|
||||
root_color = "Red" if int(self.root.color) == 0 else "Black"
|
||||
print(f" Root Color: {root_color}")
|
||||
if self.datatype:
|
||||
print(f" Data Type: {self.datatype}")
|
||||
|
||||
def print_tree(self, max_items=10):
|
||||
"""Print tree data in a readable format"""
|
||||
print(f"Red-Black Tree Contents ({len(self)} total items):")
|
||||
|
||||
count = 0
|
||||
for i, data in enumerate(self):
|
||||
if count >= max_items:
|
||||
print(f" ... showing first {max_items} of {len(self)} items")
|
||||
break
|
||||
|
||||
formatted = self.format_data(data)
|
||||
print(f" [{i}] {formatted}")
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
print(" (empty)")
|
||||
elif count < len(self):
|
||||
print(f" ... {len(self) - count} more items not shown")
|
||||
|
||||
|
||||
class LVRedBlackTreeIterator:
|
||||
"""Iterator for LVRedBlackTree that supports multiple traversals"""
|
||||
|
||||
def __init__(self, tree: LVRedBlackTree):
|
||||
self.tree = tree
|
||||
self.current = tree.minimum()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if not self.current:
|
||||
raise StopIteration
|
||||
|
||||
data = self.tree.get_data(self.current)
|
||||
|
||||
# Move to next node (in-order traversal)
|
||||
if self.current.right:
|
||||
self.current = self.tree.minimum_from(self.current.right)
|
||||
else:
|
||||
parent = self.current.parent
|
||||
while parent and self.current == parent.right:
|
||||
self.current = parent
|
||||
parent = parent.parent
|
||||
self.current = parent
|
||||
|
||||
return data
|
||||
|
||||
def __str__(self):
|
||||
"""Better string representation for iterator"""
|
||||
current = self.current
|
||||
if not current:
|
||||
return "LVRedBlackTreeIterator(ended)"
|
||||
|
||||
try:
|
||||
data = self.tree.get_data(current)
|
||||
if data:
|
||||
return f"LVRedBlackTreeIterator(current={data})"
|
||||
except:
|
||||
pass
|
||||
|
||||
return f"LVRedBlackTreeIterator(current=0x{int(current):x})"
|
||||
|
||||
|
||||
def dump_rb_info(
|
||||
rb: Union[Value, gdb.Value, int], datatype: Union[gdb.Type, str] = None
|
||||
):
|
||||
"""Dump red-black tree information"""
|
||||
tree = LVRedBlackTree(rb, datatype=datatype)
|
||||
tree.print_info()
|
||||
@@ -0,0 +1,7 @@
|
||||
from lvglgdb.value import Value
|
||||
|
||||
|
||||
def dump_style_info(style: Value):
|
||||
prop = int(style.prop)
|
||||
value = style.value
|
||||
print(f"{prop} = {value}")
|
||||
63
managed_components/lvgl__lvgl/scripts/gdb/lvglgdb/value.py
Normal file
63
managed_components/lvgl__lvgl/scripts/gdb/lvglgdb/value.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import gdb
|
||||
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
class Value(gdb.Value):
|
||||
def __init__(self, value: Union[gdb.Value, "Value"]):
|
||||
super().__init__(value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
value = super().__getitem__(key)
|
||||
except gdb.error:
|
||||
value = super().__getattr__(key)
|
||||
return Value(value)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if hasattr(super(), key):
|
||||
return Value(super().__getattribute__(key))
|
||||
return Value(super().__getitem__(key))
|
||||
|
||||
def cast(
|
||||
self, type_name: Union[str, gdb.Type], ptr: bool = False
|
||||
) -> Optional["Value"]:
|
||||
try:
|
||||
gdb_type = (
|
||||
gdb.lookup_type(type_name) if isinstance(type_name, str) else type_name
|
||||
)
|
||||
if ptr:
|
||||
gdb_type = gdb_type.pointer()
|
||||
return Value(super().cast(gdb_type))
|
||||
except gdb.error:
|
||||
return None
|
||||
|
||||
def super_value(self, attr: str) -> "Value":
|
||||
return self[attr]
|
||||
|
||||
def as_string(self):
|
||||
"""Convert to string if possible"""
|
||||
try:
|
||||
return self.string()
|
||||
except gdb.error:
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
"""Provide better string representation for debugging"""
|
||||
try:
|
||||
ptr_val = int(self)
|
||||
return f"({self.type})0x{ptr_val:x}"
|
||||
except gdb.error:
|
||||
pass
|
||||
|
||||
return super().__str__()
|
||||
|
||||
def __repr__(self):
|
||||
"""Provide detailed representation"""
|
||||
try:
|
||||
content = self.dereference()
|
||||
return f"Value({self.__str__()}: {content})"
|
||||
except gdb.error:
|
||||
pass
|
||||
|
||||
return f"Value({self.__str__()})"
|
||||
17
managed_components/lvgl__lvgl/scripts/gdb/pyproject.toml
Normal file
17
managed_components/lvgl__lvgl/scripts/gdb/pyproject.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "lvglgdb"
|
||||
version = "0.1.1"
|
||||
description = "LVGL GDB scripts"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
dependencies = [
|
||||
"numpy~=2.2.6",
|
||||
"Pillow~=11.3.0",
|
||||
"prettytable~=3.16.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://lvgl.io"
|
||||
Documentation = "https://docs.lvgl.io/master/debugging/gdb_plugin.html"
|
||||
Repository = "https://github.com/lvgl/lvgl"
|
||||
@@ -0,0 +1,3 @@
|
||||
numpy~=2.2.6
|
||||
Pillow~=11.3.0
|
||||
prettytable~=3.16.0
|
||||
File diff suppressed because one or more lines are too long
336
managed_components/lvgl__lvgl/scripts/gen_json/gen_json.py
Normal file
336
managed_components/lvgl__lvgl/scripts/gen_json/gen_json.py
Normal file
@@ -0,0 +1,336 @@
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import shutil
|
||||
import tempfile
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
base_path = os.path.abspath(os.path.dirname(__file__))
|
||||
sys.path.insert(0, base_path)
|
||||
|
||||
project_dir = os.path.abspath(os.path.join(base_path, '..', '..'))
|
||||
docs_path = os.path.join(project_dir, 'docs')
|
||||
sys.path.insert(0, docs_path)
|
||||
|
||||
import create_fake_lib_c # NOQA
|
||||
import pycparser_monkeypatch # NOQA
|
||||
import pycparser # NOQA
|
||||
|
||||
doxyfile_filename = 'Doxyfile'
|
||||
DEVELOP = False
|
||||
|
||||
|
||||
intermediate_dir = tempfile.mkdtemp(suffix='.lvgl_json')
|
||||
|
||||
|
||||
def run(output_path, lv_conf_file, output_to_stdout, target_header, filter_private, no_docstrings, *compiler_args):
|
||||
|
||||
pycparser_monkeypatch.FILTER_PRIVATE = filter_private
|
||||
|
||||
lvgl_dir = project_dir
|
||||
lvgl_src_dir = os.path.join(lvgl_dir, 'src')
|
||||
int_lvgl_dir = os.path.join(intermediate_dir, 'lvgl')
|
||||
lv_conf_dest_file = os.path.join(intermediate_dir, 'lv_conf.h')
|
||||
target_header_base_name = (
|
||||
os.path.splitext(os.path.split(target_header)[-1])[0]
|
||||
)
|
||||
|
||||
try:
|
||||
os.mkdir(int_lvgl_dir)
|
||||
shutil.copytree(lvgl_src_dir, os.path.join(int_lvgl_dir, 'src'))
|
||||
shutil.copyfile(os.path.join(lvgl_dir, 'lvgl.h'), os.path.join(int_lvgl_dir, 'lvgl.h'))
|
||||
|
||||
pp_file = os.path.join(intermediate_dir, target_header_base_name + '.pp')
|
||||
|
||||
if lv_conf_file is None:
|
||||
lv_conf_templ_file = os.path.join(lvgl_dir, 'lv_conf_template.h')
|
||||
|
||||
with open(lv_conf_templ_file, 'rb') as f:
|
||||
lines = f.read().decode('utf-8').split('\n')
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith('#if 0'):
|
||||
lines[i] = '#if 1'
|
||||
else:
|
||||
for item in (
|
||||
'LV_USE_LOG',
|
||||
'LV_USE_OBJ_ID',
|
||||
'LV_USE_OBJ_ID_BUILTIN',
|
||||
'LV_USE_FLOAT',
|
||||
'LV_USE_BIDI',
|
||||
'LV_USE_LODEPNG',
|
||||
'LV_USE_LIBPNG',
|
||||
'LV_USE_BMP',
|
||||
'LV_USE_TJPGD',
|
||||
'LV_USE_LIBJPEG_TURBO',
|
||||
'LV_USE_LIBWEBP',
|
||||
'LV_USE_GIF',
|
||||
'LV_BIN_DECODER_RAM_LOAD',
|
||||
'LV_USE_RLE',
|
||||
'LV_USE_QRCODE',
|
||||
'LV_USE_BARCODE',
|
||||
'LV_USE_TINY_TTF',
|
||||
'LV_USE_GRIDNAV',
|
||||
'LV_USE_FRAGMENT',
|
||||
'LV_USE_IMGFONT',
|
||||
'LV_USE_SNAPSHOT',
|
||||
'LV_USE_FREETYPE'
|
||||
):
|
||||
if line.startswith(f'#define {item} '):
|
||||
lines[i] = f'#define {item} 1'
|
||||
break
|
||||
|
||||
with open(lv_conf_dest_file, 'wb') as f:
|
||||
f.write('\n'.join(lines).encode('utf-8'))
|
||||
else:
|
||||
shutil.copyfile(lv_conf_file, lv_conf_dest_file)
|
||||
|
||||
include_dirs = [intermediate_dir, project_dir]
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
import get_sdl2
|
||||
|
||||
try:
|
||||
import pyMSVC # NOQA
|
||||
except ImportError:
|
||||
sys.stderr.write(
|
||||
'\nThe pyMSVC library is missing, '
|
||||
'please run "pip install pyMSVC" to install it.\n'
|
||||
)
|
||||
sys.stderr.flush()
|
||||
sys.exit(-500)
|
||||
|
||||
env = pyMSVC.setup_environment() # NOQA
|
||||
cpp_cmd = ['cl', '/std:c11', '/nologo', '/P']
|
||||
output_pp = f'/Fi"{pp_file}"'
|
||||
sdl2_include, _ = get_sdl2.get_sdl2(intermediate_dir)
|
||||
include_dirs.append(sdl2_include)
|
||||
include_path_env_key = 'INCLUDE'
|
||||
|
||||
elif sys.platform.startswith('darwin'):
|
||||
include_path_env_key = 'C_INCLUDE_PATH'
|
||||
cpp_cmd = [
|
||||
'clang', '-std=c11', '-E', '-DINT32_MIN=0x80000000',
|
||||
]
|
||||
output_pp = f' >> "{pp_file}"'
|
||||
else:
|
||||
include_path_env_key = 'C_INCLUDE_PATH'
|
||||
cpp_cmd = [
|
||||
'gcc', '-std=c11', '-E', '-Wno-incompatible-pointer-types',
|
||||
]
|
||||
output_pp = f' >> "{pp_file}"'
|
||||
|
||||
fake_libc_path = create_fake_lib_c.run(intermediate_dir)
|
||||
|
||||
if include_path_env_key not in os.environ:
|
||||
os.environ[include_path_env_key] = ''
|
||||
|
||||
os.environ[include_path_env_key] = (
|
||||
f'{fake_libc_path}{os.pathsep}{os.environ[include_path_env_key]}'
|
||||
)
|
||||
|
||||
if 'PATH' not in os.environ:
|
||||
os.environ['PATH'] = ''
|
||||
|
||||
os.environ['PATH'] = (
|
||||
f'{fake_libc_path}{os.pathsep}{os.environ["PATH"]}'
|
||||
)
|
||||
|
||||
cpp_cmd.extend(compiler_args)
|
||||
cpp_cmd.extend([
|
||||
'-DLV_LVGL_H_INCLUDE_SIMPLE',
|
||||
'-DLV_CONF_INCLUDE_SIMPLE',
|
||||
'-DLV_USE_DEV_VERSION'
|
||||
])
|
||||
|
||||
cpp_cmd.extend(['-DPYCPARSER', f'"-I{fake_libc_path}"'])
|
||||
cpp_cmd.extend([f'"-I{item}"' for item in include_dirs])
|
||||
cpp_cmd.append(f'"{target_header}"')
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
cpp_cmd.insert(len(cpp_cmd) - 2, output_pp)
|
||||
else:
|
||||
cpp_cmd.append(output_pp)
|
||||
|
||||
cpp_cmd = ' '.join(cpp_cmd)
|
||||
|
||||
p = subprocess.Popen(
|
||||
cpp_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=os.environ,
|
||||
shell=True
|
||||
)
|
||||
out, err = p.communicate()
|
||||
exit_code = p.returncode
|
||||
|
||||
if not os.path.exists(pp_file):
|
||||
sys.stdout.write(out.decode('utf-8').strip() + '\n')
|
||||
sys.stdout.write('EXIT CODE: ' + str(exit_code) + '\n')
|
||||
sys.stderr.write(err.decode('utf-8').strip() + '\n')
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
raise RuntimeError('Unknown Failure')
|
||||
|
||||
with open(pp_file, 'r') as f:
|
||||
pp_data = f.read()
|
||||
|
||||
cparser = pycparser.CParser()
|
||||
ast = cparser.parse(pp_data, target_header)
|
||||
doxyfile_src_file = os.path.join(docs_path, doxyfile_filename)
|
||||
|
||||
ast.setup_docs(no_docstrings, lvgl_src_dir,
|
||||
intermediate_dir, doxyfile_src_file, output_to_stdout)
|
||||
|
||||
if not output_to_stdout and output_path is None:
|
||||
if not DEVELOP:
|
||||
shutil.rmtree(intermediate_dir)
|
||||
|
||||
return ast
|
||||
|
||||
elif output_to_stdout:
|
||||
# stdout.reset()
|
||||
print(json.dumps(ast.to_dict(), indent=4))
|
||||
else:
|
||||
if not os.path.exists(output_path):
|
||||
os.makedirs(output_path)
|
||||
|
||||
output_path = os.path.join(output_path, target_header_base_name + '.json')
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(json.dumps(ast.to_dict(), indent=4))
|
||||
|
||||
except Exception as err:
|
||||
try:
|
||||
print(cpp_cmd) # NOQA
|
||||
print()
|
||||
except: # NOQA
|
||||
pass
|
||||
|
||||
for key, value in os.environ.items():
|
||||
print(key + ':', value)
|
||||
|
||||
print()
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
print()
|
||||
|
||||
exceptions = [
|
||||
ArithmeticError,
|
||||
AssertionError,
|
||||
AttributeError,
|
||||
EOFError,
|
||||
FloatingPointError,
|
||||
GeneratorExit,
|
||||
ImportError,
|
||||
IndentationError,
|
||||
IndexError,
|
||||
KeyError,
|
||||
KeyboardInterrupt,
|
||||
LookupError,
|
||||
MemoryError,
|
||||
NameError,
|
||||
NotImplementedError,
|
||||
OverflowError,
|
||||
ReferenceError,
|
||||
RuntimeError,
|
||||
StopIteration,
|
||||
SyntaxError,
|
||||
TabError,
|
||||
SystemExit,
|
||||
TypeError,
|
||||
UnboundLocalError,
|
||||
UnicodeError,
|
||||
UnicodeEncodeError,
|
||||
UnicodeDecodeError,
|
||||
UnicodeTranslateError,
|
||||
ValueError,
|
||||
ZeroDivisionError,
|
||||
SystemError
|
||||
]
|
||||
|
||||
if isinstance(err, OSError):
|
||||
error = err.errno
|
||||
else:
|
||||
if type(err) in exceptions:
|
||||
error = ~exceptions.index(type(err))
|
||||
else:
|
||||
error = -100
|
||||
else:
|
||||
error = 0
|
||||
|
||||
if DEVELOP:
|
||||
print('temporary file path:', intermediate_dir)
|
||||
else:
|
||||
shutil.rmtree(intermediate_dir)
|
||||
|
||||
sys.exit(error)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser('-')
|
||||
parser.add_argument(
|
||||
'--output-path',
|
||||
dest='output_path',
|
||||
help=(
|
||||
'output directory for JSON file. If one is not '
|
||||
'supplied then it will be output stdout'
|
||||
),
|
||||
action='store',
|
||||
default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
'--lvgl-config',
|
||||
dest='lv_conf',
|
||||
help=(
|
||||
'path to lv_conf.h (including file name), if this is not set then '
|
||||
'a config file will be generated that has everything turned on.'
|
||||
),
|
||||
action='store',
|
||||
default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
'--develop',
|
||||
dest='develop',
|
||||
help='this option leaves the temporary folder in place.',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target-header",
|
||||
dest="target_header",
|
||||
help=(
|
||||
"path to a custom header file. When using this to supply a custom"
|
||||
"header file you MUST insure that any LVGL includes are done so "
|
||||
"they are relitive to the LVGL repository root folder.\n\n"
|
||||
'#include "src/lvgl_private.h"\n\n'
|
||||
"If you have includes to header files that are not LVGL then you "
|
||||
"will need to add the include locations for those header files "
|
||||
"when running this script. It is done using the same manner that "
|
||||
"is used when calling a C compiler\n\n"
|
||||
"You need to provide the absolute path to the header file when "
|
||||
"using this feature."
|
||||
),
|
||||
action="store",
|
||||
default=os.path.join(intermediate_dir, "lvgl", "lvgl.h")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter-private',
|
||||
dest='filter_private',
|
||||
help='Internal Use',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-docstrings',
|
||||
dest='no_docstrings',
|
||||
help='Internal Use',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
args, extra_args = parser.parse_known_args()
|
||||
|
||||
DEVELOP = args.develop
|
||||
|
||||
run(args.output_path, args.lv_conf, args.output_path is None, args.target_header, args.filter_private, args.no_docstrings, *extra_args)
|
||||
66
managed_components/lvgl__lvgl/scripts/gen_json/get_sdl2.py
Normal file
66
managed_components/lvgl__lvgl/scripts/gen_json/get_sdl2.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import zipfile
|
||||
import io
|
||||
import os
|
||||
|
||||
|
||||
SDL2_URL = 'https://github.com/libsdl-org/SDL/releases/download/release-2.26.5/SDL2-devel-2.26.5-VC.zip' # NOQA
|
||||
|
||||
|
||||
def get_path(name: str, p: str) -> str:
|
||||
for file in os.listdir(p):
|
||||
file = os.path.join(p, file)
|
||||
|
||||
if file.endswith(name):
|
||||
return file
|
||||
|
||||
if os.path.isdir(file):
|
||||
if res := get_path(name, file):
|
||||
return res
|
||||
|
||||
|
||||
def get_sdl2(path, url=SDL2_URL):
|
||||
import requests # NOQA
|
||||
|
||||
stream = io.BytesIO()
|
||||
|
||||
with requests.get(url, stream=True) as r:
|
||||
r.raise_for_status()
|
||||
|
||||
content_length = int(r.headers['Content-Length'])
|
||||
chunks = 0
|
||||
# print()
|
||||
# sys.stdout.write('\r' + str(chunks) + '/' + str(content_length))
|
||||
# sys.stdout.flush()
|
||||
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
stream.write(chunk)
|
||||
chunks += len(chunk)
|
||||
# sys.stdout.write('\r' + str(chunks) + '/' + str(content_length))
|
||||
# sys.stdout.flush()
|
||||
|
||||
# print()
|
||||
stream.seek(0)
|
||||
zf = zipfile.ZipFile(stream)
|
||||
|
||||
for z_item in zf.infolist():
|
||||
for ext in ('.h', '.dll', '.lib'):
|
||||
if not z_item.filename.endswith(ext):
|
||||
continue
|
||||
|
||||
zf.extract(z_item, path=path)
|
||||
break
|
||||
|
||||
include_path = get_path('include', path)
|
||||
lib_path = get_path('lib\\x64', path)
|
||||
dll_path = get_path('SDL2.dll', lib_path)
|
||||
|
||||
sdl_include_path = os.path.split(include_path)[0]
|
||||
if not os.path.exists(os.path.join(sdl_include_path, 'SDL2')):
|
||||
os.rename(include_path, os.path.join(sdl_include_path, 'SDL2'))
|
||||
|
||||
zf.close()
|
||||
stream.close()
|
||||
|
||||
return os.path.abspath(sdl_include_path), dll_path
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
pycparser>=2.22
|
||||
pyMSVC>=0.5.3; platform_system == "Windows"
|
||||
Sphinx
|
||||
breathe
|
||||
imagesize
|
||||
importlib-metadata
|
||||
sphinx-rtd-theme
|
||||
sphinx-sitemap
|
||||
sphinxcontrib-applehelp
|
||||
sphinxcontrib-devhelp
|
||||
sphinxcontrib-htmlhelp
|
||||
sphinxcontrib-jsmath
|
||||
sphinxcontrib-qthelp
|
||||
sphinxcontrib-serializinghtml
|
||||
sphinx-rtd-dark-mode
|
||||
typing-extensions
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
#
|
||||
# Generate the cmake variables CONFIG_LV_USE_* or CONFIG_LV_BUILD_* from the
|
||||
# preprocessed lv_conf_internal.h
|
||||
#
|
||||
# Author: David TRUAN (david.truan@edgemtech.ch)
|
||||
# Author: Erik Tagirov (erik.tagirov@edgemtech.ch)
|
||||
#
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import re
|
||||
|
||||
def fatal(msg):
|
||||
print()
|
||||
print("ERROR! " + msg)
|
||||
exit(1)
|
||||
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=""
|
||||
"Convert the expanded lv_conf_internal.h to cmake variables."
|
||||
"It converts all LV_USE_*, LV_BUILD_* configurations."
|
||||
)
|
||||
|
||||
parser.add_argument('--input', type=str, required=True, nargs='?',
|
||||
help='Path of the macro expanded lv_conf_internal.h, which should be generated during a cmake build')
|
||||
|
||||
parser.add_argument('--output', type=str, required=True, nargs='?',
|
||||
help='Path of the output file, where the cmake variables declaration will be written (ex: build/lv_conf.cmake)')
|
||||
|
||||
parser.add_argument("--kconfig", action="store_true", help="Enable kconfig flag")
|
||||
|
||||
parser.add_argument("--debug", action="store_true", required=False, help="Show unhandled expressions")
|
||||
|
||||
parser.add_argument("--parentscope", action="store_true", required=False, help="Additionally set the variables in the parent scope")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# The input must exist
|
||||
if not os.path.exists(args.input):
|
||||
fatal(f"Input {args.input} not found")
|
||||
|
||||
return args
|
||||
|
||||
def write_set_cmd(fout, expr, is_parent_scope):
|
||||
|
||||
fout.write(f'set({expr})\n')
|
||||
|
||||
# This makes the variable usable from the top level directory
|
||||
if is_parent_scope == True:
|
||||
fout.write(f'set({expr} PARENT_SCOPE)\n')
|
||||
|
||||
def generate_cmake_variables(path_input: str, path_output: str, kconfig: bool, debug: bool, is_parent_scope: bool):
|
||||
fin = open(path_input)
|
||||
fout = open(path_output, "w", newline='')
|
||||
|
||||
# If we use Kconfig, we must check the CONFIG_LV_USE_* and
|
||||
# CONFIG_LV_BUILD_* defines
|
||||
if kconfig:
|
||||
CONFIG_PATTERN="^#define +(CONFIG_LV_USE|CONFIG_LV_BUILD|CONFIG_LV_[0-9A-Z_]+_USE)"
|
||||
CONFIG_PREFIX=""
|
||||
# Otherwise check the LV_USE_* and LV_BUILD_* defines
|
||||
else:
|
||||
CONFIG_PATTERN="^#define +(LV_USE|LV_BUILD|LV_[0-9A-Z_]+_USE)"
|
||||
CONFIG_PREFIX="CONFIG_"
|
||||
|
||||
|
||||
# Using the expanded lv_conf_internal, we don't have to deal with regexp,
|
||||
# as all the #define will be aligned on the left with a single space before the value
|
||||
for line in fin.read().splitlines():
|
||||
|
||||
# Treat the LV_USE_STDLIB_* configs in a special way, as we need
|
||||
# to convert the define to full config with 1 value when enabled
|
||||
if re.search(f'{CONFIG_PATTERN}_STDLIB', line):
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
name = parts[1]
|
||||
value = parts[2].strip()
|
||||
|
||||
type = value.split("LV_STDLIB_")[1]
|
||||
|
||||
name = name.replace("STDLIB", type)
|
||||
|
||||
write_set_cmd(fout, f'{CONFIG_PREFIX}{name} 1', is_parent_scope)
|
||||
|
||||
# Treat the LV_USE_OS config in a special way, as we need
|
||||
# to convert the define to full config with 1 value when enabled
|
||||
if re.search(f'{CONFIG_PATTERN}_OS', line):
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
name = parts[1]
|
||||
value = parts[2].strip()
|
||||
|
||||
type = value.split("LV_OS")[1]
|
||||
|
||||
name += type
|
||||
|
||||
write_set_cmd(fout, f'{CONFIG_PREFIX}{name} 1', is_parent_scope)
|
||||
|
||||
# For the rest of the configs, simply add CONFIG_ and write the name of the define
|
||||
# all LV_USE_* or LV_BUILD_* configs where the value is 0 or 1,
|
||||
# as these are the ones that are needed in cmake
|
||||
# To detect the configuration of LVGL to perform conditional compilation/linking
|
||||
elif re.search(f'{CONFIG_PATTERN}.* +[01] *$', line):
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
name = parts[1]
|
||||
value = parts[2].strip()
|
||||
|
||||
write_set_cmd(fout, f'{CONFIG_PREFIX}{name} {value}', is_parent_scope)
|
||||
|
||||
else:
|
||||
# Useful for debugging expressions that are unhandled,
|
||||
# if the script fails in 'unexpected ways'
|
||||
if debug == True:
|
||||
print(f"DBG: Skipping expression: '{line} - not handled'")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_args()
|
||||
|
||||
generate_cmake_variables(args.input, args.output, args.kconfig, args.debug, args.parentscope)
|
||||
125
managed_components/lvgl__lvgl/scripts/generate_lv_conf.py
Normal file
125
managed_components/lvgl__lvgl/scripts/generate_lv_conf.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
DIR_SCRIPTS = os.path.dirname(__file__)
|
||||
REPO_ROOT = os.path.join(DIR_SCRIPTS, "..")
|
||||
DIR_CWD = os.getcwd()
|
||||
|
||||
|
||||
def fatal(msg):
|
||||
print()
|
||||
print("ERROR! " + msg)
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=""
|
||||
"Update the configuration file of your existing project based on default parameters and latest LVGL template. Eg.:\n"
|
||||
" python3 generate_lv_conf.py ./src \n"
|
||||
" python3 generate_lv_conf.py --template modules/lvgl/lv_conf_template.h ./my_config_folder"
|
||||
)
|
||||
|
||||
parser.add_argument('--template', type=str, default=REPO_ROOT, nargs='?',
|
||||
help='Path of "lv_conf_template.h" file or the folder containing it\n(optional, 1 folder above the python script by default)')
|
||||
|
||||
parser.add_argument('--config', type=str, default=None, nargs='?',
|
||||
help='Path of "lv_conf.h" file (optional)')
|
||||
|
||||
parser.add_argument('--defaults', type=str, default=None, nargs='?',
|
||||
help='Path of "lv_conf.defaults" file (optional)')
|
||||
|
||||
parser.add_argument('target', metavar='target', type=str, default=DIR_CWD, nargs='?',
|
||||
help='Folder containing "lv_conf.h" and "lv_conf.defaults" files\n(optional, current work folder by default)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if os.path.isdir(args.template):
|
||||
args.template = os.path.join(args.template, "lv_conf_template.h")
|
||||
|
||||
if not args.config:
|
||||
args.config = os.path.join(args.target, "lv_conf.h")
|
||||
|
||||
if not args.defaults:
|
||||
args.defaults = os.path.join(args.target, "lv_conf.defaults")
|
||||
|
||||
if not os.path.exists(args.template):
|
||||
fatal(f"Template file not found at {args.template}")
|
||||
if not os.path.exists(args.config):
|
||||
print(f"{args.config} not found. Generating it")
|
||||
Path(args.config).touch()
|
||||
if not os.path.exists(args.defaults):
|
||||
fatal(f"User defaults not found at {args.defaults}")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def parse_defaults(path: str):
|
||||
defaults = {}
|
||||
|
||||
with open(path, 'r', encoding='utf-8') as file:
|
||||
for line in file.readlines():
|
||||
if len(line.strip()) == 0 or line.startswith('#'):
|
||||
continue
|
||||
search_result = re.search(r'([A-Z0-9_]+)\s+(.+)', line)
|
||||
if not search_result:
|
||||
print(f"WARNING: Ignoring line {line}")
|
||||
continue
|
||||
groups = search_result.groups()
|
||||
defaults[groups[0]] = groups[1]
|
||||
|
||||
return defaults
|
||||
|
||||
|
||||
def generate_config(path_destination: str, path_source: str, defaults: dict):
|
||||
with open(path_source, 'r', encoding='utf-8') as f_src:
|
||||
src_lines = f_src.readlines()
|
||||
|
||||
keys_used = set()
|
||||
dst_lines = []
|
||||
|
||||
for src_line in src_lines:
|
||||
res = re.search(r'#define\s+([A-Z0-9_]+)', src_line)
|
||||
key = res.groups()[0] if res else None
|
||||
|
||||
if key in defaults.keys():
|
||||
value = defaults[key]
|
||||
pattern = r'(#define\s+[A-Z0-9_]+[^\S\r\n]*)[^\n]*'
|
||||
has_space = re.search(pattern, src_line).groups()[0][-1].isspace()
|
||||
repl = r'\g<1>' + value if has_space else r'\g<1> ' + value
|
||||
dst_line, _ = re.subn(pattern, repl, src_line)
|
||||
|
||||
print(f"Applying: {key} = {value}")
|
||||
keys_used.add(key)
|
||||
elif 'Set this to "1" to enable content' in src_line:
|
||||
dst_line = '#if 1 /* Enable content */'
|
||||
else:
|
||||
dst_line = src_line
|
||||
|
||||
dst_lines.append(dst_line)
|
||||
|
||||
if len(keys_used) != len(defaults):
|
||||
unused_keys = [k for k in defaults.keys() if k not in keys_used]
|
||||
print('WARNING: The following keys are deprecated:\n ' + '\n '.join(unused_keys))
|
||||
|
||||
with open(path_destination, 'w', encoding='utf-8') as f_dst:
|
||||
for dst_line in dst_lines:
|
||||
f_dst.write(dst_line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_args()
|
||||
|
||||
print("Template:", args.template)
|
||||
print("User config:", args.config)
|
||||
print("User defaults:", args.defaults)
|
||||
|
||||
defaults = parse_defaults(args.defaults)
|
||||
print(f"Loaded {len(defaults)} defaults")
|
||||
|
||||
generate_config(args.config, args.template, defaults)
|
||||
print()
|
||||
print('New config successfully generated!')
|
||||
15
managed_components/lvgl__lvgl/scripts/genexamplelist.sh
Normal file
15
managed_components/lvgl__lvgl/scripts/genexamplelist.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
echo "/* Autogenerated */"
|
||||
echo '#include <stddef.h>'
|
||||
echo '#include "examplelist.h"'
|
||||
TMPFILE=$(mktemp)
|
||||
find examples demos -name \*.h | xargs grep -hE "^void lv_(example|demo)" | sed 's/(/ /g' | awk '{print $2}' > $TMPFILE
|
||||
cat $TMPFILE | while read -r line; do
|
||||
echo "extern void ${line}(void);"
|
||||
done
|
||||
echo "const struct lv_ci_example lv_ci_example_list[] = {"
|
||||
cat $TMPFILE | while read -r line; do
|
||||
echo " { \"$line\", $line },";
|
||||
done
|
||||
echo " { NULL, NULL }"
|
||||
echo "};"
|
||||
31
managed_components/lvgl__lvgl/scripts/image_viewer.py
Normal file
31
managed_components/lvgl__lvgl/scripts/image_viewer.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
from LVGLImage import LVGLImage
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
parser = argparse.ArgumentParser(description="LVGL Binary Image Viewer")
|
||||
parser.add_argument("file", help="the .bin image file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
name, ext = os.path.splitext(args.file)
|
||||
if ext != ".bin":
|
||||
raise ValueError("Only support LVGL .bin image file")
|
||||
|
||||
output = name + ".png"
|
||||
img = LVGLImage().from_bin(args.file)
|
||||
img.to_png(output)
|
||||
logging.info(f"convert {args.file} to {output}")
|
||||
|
||||
if os.name == "posix":
|
||||
os.system(f"open {output}")
|
||||
else:
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
raise ImportError("Need pillow package, do `pip3 install pillow`")
|
||||
image = Image.open(output)
|
||||
image.show(title=output)
|
||||
10
managed_components/lvgl__lvgl/scripts/infer_run.sh
Normal file
10
managed_components/lvgl__lvgl/scripts/infer_run.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# https://github.com/facebook/infer
|
||||
#
|
||||
# Install:
|
||||
# VERSION=0.17.0; \
|
||||
# curl -sSL "https://github.com/facebook/infer/releases/download/v$VERSION/infer-linux64-v$VERSION.tar.xz" \
|
||||
# | sudo tar -C /opt -xJ && \
|
||||
# sudoln -s "/opt/infer-linux64-v$VERSION/bin/infer" /usr/local/bin/infer
|
||||
|
||||
infer run -- make -j8
|
||||
@@ -0,0 +1,4 @@
|
||||
vcpkg install vcpkg-tool-ninja libpng freetype opengl glfw3
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
pip install pypng lz4 kconfiglib
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Install Linux package prerequisites needed for LVGL development
|
||||
# and testing. Some less-common development packages are not included
|
||||
# here, such as MicroPython and PC simulator packages.
|
||||
#
|
||||
# Note: This script is run by the CI workflows.
|
||||
SCRIPT_PATH=$(readlink -f $0)
|
||||
SCRIPT_DIR=$(dirname $SCRIPT_PATH)
|
||||
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update
|
||||
|
||||
cat $SCRIPT_DIR/prerequisites-apt.txt | xargs sudo apt install -y
|
||||
pip3 install --user -r $SCRIPT_DIR/prerequisites-pip.txt
|
||||
16
managed_components/lvgl__lvgl/scripts/install_astyle.sh
Normal file
16
managed_components/lvgl__lvgl/scripts/install_astyle.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check if the script is being run as root
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run as root or with sudo" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf astyle
|
||||
git clone https://github.com/lvgl/astyle.git
|
||||
cd astyle/build/gcc
|
||||
git checkout v3.4.12
|
||||
make -j
|
||||
make install
|
||||
cd ../../..
|
||||
rm -rf astyle
|
||||
11
managed_components/lvgl__lvgl/scripts/install_pngquant.sh
Normal file
11
managed_components/lvgl__lvgl/scripts/install_pngquant.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf pngquant
|
||||
git clone https://github.com/kornelski/pngquant
|
||||
cd pngquant
|
||||
git checkout 2.17.0
|
||||
./configure
|
||||
make -j
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf pngquant
|
||||
140
managed_components/lvgl__lvgl/scripts/jpg_to_sjpg.py
Normal file
140
managed_components/lvgl__lvgl/scripts/jpg_to_sjpg.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
##################################################################
|
||||
# sjpeg converter script version 1.0
|
||||
# Dependencies: (PYTHON-3)
|
||||
##################################################################
|
||||
SJPG_FILE_FORMAT_VERSION = "V1.00" #
|
||||
JPEG_SPLIT_HEIGHT = 16
|
||||
##################################################################
|
||||
import math, os, sys, time
|
||||
from PIL import Image
|
||||
|
||||
|
||||
OUTPUT_FILE_NAME = ""
|
||||
INPUT_FILE = ""
|
||||
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
INPUT_FILE = sys.argv[1]
|
||||
OUTPUT_FILE_NAME = INPUT_FILE.split("/")[-1].split("\\")[-1].split(".")[0]
|
||||
else:
|
||||
print("usage:\n\t python " + sys.argv[0] + " input_file.jpg")
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
im = Image.open(INPUT_FILE)
|
||||
except:
|
||||
print("\nFile not found!")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
print("\nConversion started...\n")
|
||||
start_time = time.time()
|
||||
width, height = im.size
|
||||
|
||||
print("Input:")
|
||||
print("\t" + INPUT_FILE)
|
||||
print("\tRES = " + str(width) + " x " + str(height) + '\n')
|
||||
|
||||
|
||||
lenbuf = []
|
||||
block_size = JPEG_SPLIT_HEIGHT;
|
||||
splits = math.ceil(height/block_size)
|
||||
|
||||
c_code = '''//LVGL SJPG C ARRAY\n#include "lvgl/lvgl.h"\n\nconst uint8_t ''' + OUTPUT_FILE_NAME + '''_map[] = {\n'''
|
||||
|
||||
sjpeg_data = bytearray()
|
||||
sjpeg = bytearray()
|
||||
|
||||
|
||||
row_remaining = height;
|
||||
for i in range(splits):
|
||||
if row_remaining < block_size:
|
||||
crop = im.crop((0, i*block_size, width, row_remaining + i*block_size))
|
||||
else:
|
||||
crop = im.crop((0, i*block_size, width, block_size + i*block_size))
|
||||
|
||||
row_remaining = row_remaining - block_size;
|
||||
crop.save(str(i)+".jpg", quality=90)
|
||||
|
||||
|
||||
|
||||
|
||||
for i in range(splits):
|
||||
f = open(str(i)+".jpg", "rb")
|
||||
a = f.read()
|
||||
f.close()
|
||||
sjpeg_data = sjpeg_data + a
|
||||
lenbuf.append(len(a))
|
||||
|
||||
header = bytearray()
|
||||
|
||||
#4 BYTES
|
||||
header = header + bytearray("_SJPG__".encode("UTF-8"));
|
||||
|
||||
#6 BYTES VERSION
|
||||
header = header + bytearray(("\x00" + SJPG_FILE_FORMAT_VERSION + "\x00").encode("UTF-8"));
|
||||
|
||||
#WIDTH 2 BYTES
|
||||
header = header + width.to_bytes(2, byteorder='little');
|
||||
|
||||
#HEIGHT 2 BYTES
|
||||
header = header + height.to_bytes(2, byteorder='little');
|
||||
|
||||
#NUMBER OF ITEMS 2 BYTES
|
||||
header = header + splits.to_bytes(2, byteorder='little');
|
||||
|
||||
#NUMBER OF ITEMS 2 BYTES
|
||||
header = header + int(JPEG_SPLIT_HEIGHT).to_bytes(2, byteorder='little');
|
||||
|
||||
for item_len in lenbuf:
|
||||
# WIDTH 2 BYTES
|
||||
header = header + item_len.to_bytes(2, byteorder='little');
|
||||
|
||||
|
||||
data = bytearray()
|
||||
|
||||
sjpeg = header + sjpeg_data;
|
||||
|
||||
if 1:
|
||||
for i in range(len(lenbuf)):
|
||||
os.remove(str(i) + ".jpg")
|
||||
|
||||
|
||||
f = open(OUTPUT_FILE_NAME+".sjpg","wb");
|
||||
f.write(sjpeg)
|
||||
f.close()
|
||||
|
||||
new_line_threshold = 0
|
||||
for i in range(len(sjpeg)):
|
||||
c_code = c_code + "\t" + str(hex(sjpeg[i])) + ","
|
||||
new_line_threshold = new_line_threshold + 1
|
||||
if (new_line_threshold >= 16):
|
||||
c_code = c_code + "\n"
|
||||
new_line_threshold = 0
|
||||
|
||||
|
||||
c_code = c_code + "\n};\n\nlv_image_dsc_t "
|
||||
c_code = c_code + OUTPUT_FILE_NAME + " = {\n"
|
||||
c_code = c_code + "\t.header.always_zero = 0,\n"
|
||||
c_code = c_code + "\t.header.w = " + str(width) + ",\n"
|
||||
c_code = c_code + "\t.header.h = " + str(height) + ",\n"
|
||||
c_code = c_code + "\t.data_size = " + str(len(sjpeg)) + ",\n"
|
||||
c_code = c_code + "\t.header.cf = LV_IMG_CF_RAW,\n"
|
||||
c_code = c_code + "\t.data = " + OUTPUT_FILE_NAME+"_map" + ",\n};"
|
||||
|
||||
|
||||
f = open(OUTPUT_FILE_NAME + '.c', 'w')
|
||||
f.write(c_code)
|
||||
f.close()
|
||||
|
||||
|
||||
time_taken = (time.time() - start_time)
|
||||
|
||||
print("Output:")
|
||||
print("\tTime taken = " + str(round(time_taken,2)) + " sec")
|
||||
print("\tbin size = " + str(round(len(sjpeg)/1024, 1)) + " KB" )
|
||||
print("\t" + OUTPUT_FILE_NAME + ".sjpg\t(bin file)" + "\n\t" + OUTPUT_FILE_NAME + ".c\t\t(c array)")
|
||||
|
||||
print("\nAll good!")
|
||||
346
managed_components/lvgl__lvgl/scripts/kconfig.py
Normal file
346
managed_components/lvgl__lvgl/scripts/kconfig.py
Normal file
@@ -0,0 +1,346 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Originally modified from:
|
||||
# https://github.com/zephyrproject-rtos/zephyr/blob/main/scripts/kconfig/kconfig.py
|
||||
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
# Writes/updates the lvgl/.config configuration file by merging configuration
|
||||
# files passed as arguments
|
||||
#
|
||||
# When fragments haven't changed, lvgl/.config is both the input and the
|
||||
# output, which just updates it. This is handled in the CMake files.
|
||||
#
|
||||
# Also does various checks (most via Kconfiglib warnings).
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
# Lvgl doesn't use tristate symbols. They're supported here just to make the
|
||||
# script a bit more generic.
|
||||
from kconfiglib import (
|
||||
Kconfig,
|
||||
split_expr,
|
||||
expr_value,
|
||||
expr_str,
|
||||
BOOL,
|
||||
TRISTATE,
|
||||
TRI_TO_STR,
|
||||
AND,
|
||||
OR,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
print(sys.argv)
|
||||
args = parse_args()
|
||||
|
||||
print("Parsing " + args.kconfig_file)
|
||||
kconf = Kconfig(args.kconfig_file, warn_to_stderr=False, suppress_traceback=True)
|
||||
|
||||
if args.handwritten_input_configs:
|
||||
# Warn for assignments to undefined symbols, but only for handwritten
|
||||
# fragments, to avoid warnings-turned-errors when using an old
|
||||
# configuration file together with updated Kconfig files
|
||||
kconf.warn_assign_undef = True
|
||||
|
||||
# prj.conf may override settings from the board configuration, so
|
||||
# disable warnings about symbols being assigned more than once
|
||||
kconf.warn_assign_override = False
|
||||
kconf.warn_assign_redun = False
|
||||
|
||||
if args.forced_input_configs:
|
||||
# Do not warn on a redundant config.
|
||||
# The reason is that a regular .config will be followed by the forced
|
||||
# config which under normal circumstances should be identical to the
|
||||
# configured setting.
|
||||
# Only if user has modified to a value that gets overruled by the forced
|
||||
# a warning shall be issued.
|
||||
kconf.warn_assign_redun = False
|
||||
|
||||
# Load files
|
||||
print(kconf.load_config(args.configs_in[0]))
|
||||
for config in args.configs_in[1:]:
|
||||
# replace=False creates a merged configuration
|
||||
print(kconf.load_config(config, replace=False))
|
||||
|
||||
if args.handwritten_input_configs:
|
||||
# Check that there are no assignments to promptless symbols, which
|
||||
# have no effect.
|
||||
#
|
||||
# This only makes sense when loading handwritten fragments and not when
|
||||
# loading lvgl/.config, because lvgl/.config is configuration
|
||||
# output and also assigns promptless symbols.
|
||||
check_no_promptless_assign(kconf)
|
||||
|
||||
# Print warnings for symbols that didn't get the assigned value. Only
|
||||
# do this for handwritten input too, to avoid likely unhelpful warnings
|
||||
# when using an old configuration and updating Kconfig files.
|
||||
check_assigned_sym_values(kconf)
|
||||
check_assigned_choice_values(kconf)
|
||||
|
||||
if kconf.syms.get("WARN_DEPRECATED", kconf.y).tri_value == 2:
|
||||
check_deprecated(kconf)
|
||||
|
||||
if kconf.syms.get("WARN_EXPERIMENTAL", kconf.y).tri_value == 2:
|
||||
check_experimental(kconf)
|
||||
|
||||
# Hack: Force all symbols to be evaluated, to catch warnings generated
|
||||
# during evaluation. Wait till the end to write the actual output files, so
|
||||
# that we don't generate any output if there are warnings-turned-errors.
|
||||
#
|
||||
# Kconfiglib caches calculated symbol values internally, so this is still
|
||||
# fast.
|
||||
kconf.write_config(os.devnull)
|
||||
|
||||
warn_only = r"warning:.*set more than once."
|
||||
|
||||
if kconf.warnings:
|
||||
if args.forced_input_configs:
|
||||
error_out = False
|
||||
else:
|
||||
error_out = True
|
||||
|
||||
# Put a blank line between warnings to make them easier to read
|
||||
for warning in kconf.warnings:
|
||||
print("\n" + warning, file=sys.stderr)
|
||||
|
||||
if not error_out and not re.search(warn_only, warning):
|
||||
# The warning is not a warn_only, fail the Kconfig.
|
||||
error_out = True
|
||||
|
||||
# Turn all warnings into errors, so that e.g. assignments to undefined
|
||||
# Kconfig symbols become errors.
|
||||
#
|
||||
# A warning is generated by this script whenever a symbol gets a
|
||||
# different value than the one it was assigned. Keep that one as just a
|
||||
# warning for now.
|
||||
if error_out:
|
||||
err("Aborting due to Kconfig warnings")
|
||||
|
||||
# Write the merged configuration and the C header
|
||||
print(kconf.write_config(args.config_out))
|
||||
print(kconf.write_autoconf(args.header_out))
|
||||
|
||||
# Write the list of parsed Kconfig files to a file
|
||||
write_kconfig_filenames(kconf, args.kconfig_list_out)
|
||||
|
||||
|
||||
def check_no_promptless_assign(kconf):
|
||||
# Checks that no promptless symbols are assigned
|
||||
|
||||
for sym in kconf.unique_defined_syms:
|
||||
if sym.user_value is not None and promptless(sym):
|
||||
err(
|
||||
f"""\
|
||||
{sym.name_and_loc} is assigned in a configuration file, but is not directly
|
||||
user-configurable (has no prompt). It gets its value indirectly from other
|
||||
symbols. """
|
||||
+ SYM_INFO_HINT.format(sym)
|
||||
)
|
||||
|
||||
|
||||
def check_assigned_sym_values(kconf):
|
||||
# Verifies that the values assigned to symbols "took" (matches the value
|
||||
# the symbols actually got), printing warnings otherwise. Choice symbols
|
||||
# are checked separately, in check_assigned_choice_values().
|
||||
|
||||
for sym in kconf.unique_defined_syms:
|
||||
if sym.choice:
|
||||
continue
|
||||
|
||||
user_value = sym.user_value
|
||||
if user_value is None:
|
||||
continue
|
||||
|
||||
# Tristate values are represented as 0, 1, 2. Having them as "n", "m",
|
||||
# "y" is more convenient here, so convert.
|
||||
if sym.type in (BOOL, TRISTATE):
|
||||
user_value = TRI_TO_STR[user_value]
|
||||
|
||||
if user_value != sym.str_value:
|
||||
msg = (
|
||||
f"{sym.name_and_loc} was assigned the value '{user_value}'"
|
||||
f" but got the value '{sym.str_value}'. "
|
||||
)
|
||||
|
||||
# List any unsatisfied 'depends on' dependencies in the warning
|
||||
mdeps = missing_deps(sym)
|
||||
if mdeps:
|
||||
expr_strs = []
|
||||
for expr in mdeps:
|
||||
estr = expr_str(expr)
|
||||
if isinstance(expr, tuple):
|
||||
# Add () around dependencies that aren't plain symbols.
|
||||
# Gives '(FOO || BAR) (=n)' instead of
|
||||
# 'FOO || BAR (=n)', which might be clearer.
|
||||
estr = f"({estr})"
|
||||
expr_strs.append(f"{estr} " f"(={TRI_TO_STR[expr_value(expr)]})")
|
||||
|
||||
msg += (
|
||||
"Check these unsatisfied dependencies: "
|
||||
+ ", ".join(expr_strs)
|
||||
+ ". "
|
||||
)
|
||||
|
||||
warn(msg + SYM_INFO_HINT.format(sym))
|
||||
|
||||
|
||||
def missing_deps(sym):
|
||||
# check_assigned_sym_values() helper for finding unsatisfied dependencies.
|
||||
#
|
||||
# Given direct dependencies
|
||||
#
|
||||
# depends on <expr> && <expr> && ... && <expr>
|
||||
#
|
||||
# on 'sym' (which can also come from e.g. a surrounding 'if'), returns a
|
||||
# list of all <expr>s with a value less than the value 'sym' was assigned
|
||||
# ("less" instead of "not equal" just to be general and handle tristates,
|
||||
# even though lvgl doesn't use them).
|
||||
#
|
||||
# For string/int/hex symbols, just looks for <expr> = n.
|
||||
#
|
||||
# Note that <expr>s can be something more complicated than just a symbol,
|
||||
# like 'FOO || BAR' or 'FOO = "string"'.
|
||||
|
||||
deps = split_expr(sym.direct_dep, AND)
|
||||
|
||||
if sym.type in (BOOL, TRISTATE):
|
||||
return [dep for dep in deps if expr_value(dep) < sym.user_value]
|
||||
# string/int/hex
|
||||
return [dep for dep in deps if expr_value(dep) == 0]
|
||||
|
||||
|
||||
def check_assigned_choice_values(kconf):
|
||||
# Verifies that any choice symbols that were selected (by setting them to
|
||||
# y) ended up as the selection, printing warnings otherwise.
|
||||
#
|
||||
# We check choice symbols separately to avoid warnings when two different
|
||||
# choice symbols within the same choice are set to y. This might happen if
|
||||
# a choice selection from a board defconfig is overridden in a prj.conf,
|
||||
# for example. The last choice symbol set to y becomes the selection (and
|
||||
# all other choice symbols get the value n).
|
||||
#
|
||||
# Without special-casing choices, we'd detect that the first symbol set to
|
||||
# y ended up as n, and print a spurious warning.
|
||||
|
||||
for choice in kconf.unique_choices:
|
||||
if choice.user_selection and choice.user_selection is not choice.selection:
|
||||
|
||||
warn(
|
||||
f"""\
|
||||
The choice symbol {choice.user_selection.name_and_loc} was selected (set =y),
|
||||
but {choice.selection.name_and_loc if choice.selection else "no symbol"} ended
|
||||
up as the choice selection. """
|
||||
+ SYM_INFO_HINT.format(choice.user_selection)
|
||||
)
|
||||
|
||||
|
||||
# Hint on where to find symbol information. Used like
|
||||
# SYM_INFO_HINT.format(sym).
|
||||
SYM_INFO_HINT = """\
|
||||
See https://docs.lvgl.io/master/integration/overview/configuration.html
|
||||
look up {0.name} in the menuconfig/guiconfig interface. The Application
|
||||
Development Primer, Setting Configuration Values, and Kconfig - Tips and Best
|
||||
Practices sections of the manual might be helpful too.\
|
||||
"""
|
||||
|
||||
|
||||
def check_deprecated(kconf):
|
||||
deprecated = kconf.syms.get("DEPRECATED")
|
||||
dep_expr = kconf.n if deprecated is None else deprecated.rev_dep
|
||||
|
||||
if dep_expr is not kconf.n:
|
||||
selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
|
||||
for selector in selectors:
|
||||
selector_name = split_expr(selector, AND)[0].name
|
||||
warn(f"Deprecated symbol {selector_name} is enabled.")
|
||||
|
||||
|
||||
def check_experimental(kconf):
|
||||
experimental = kconf.syms.get("EXPERIMENTAL")
|
||||
dep_expr = kconf.n if experimental is None else experimental.rev_dep
|
||||
|
||||
if dep_expr is not kconf.n:
|
||||
selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
|
||||
for selector in selectors:
|
||||
selector_name = split_expr(selector, AND)[0].name
|
||||
warn(f"Experimental symbol {selector_name} is enabled.")
|
||||
|
||||
|
||||
def promptless(sym):
|
||||
# Returns True if 'sym' has no prompt. Since the symbol might be defined in
|
||||
# multiple locations, we need to check all locations.
|
||||
|
||||
return not any(node.prompt for node in sym.nodes)
|
||||
|
||||
|
||||
def write_kconfig_filenames(kconf, kconfig_list_path):
|
||||
# Writes a sorted list with the absolute paths of all parsed Kconfig files
|
||||
# to 'kconfig_list_path'. The paths are realpath()'d, and duplicates are
|
||||
# removed. This file is used by CMake to look for changed Kconfig files. It
|
||||
# needs to be deterministic.
|
||||
|
||||
with open(kconfig_list_path, "w") as out:
|
||||
for path in sorted(
|
||||
{
|
||||
os.path.realpath(os.path.join(kconf.srctree, path))
|
||||
for path in kconf.kconfig_filenames
|
||||
}
|
||||
):
|
||||
print(path, file=out)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False)
|
||||
|
||||
parser.add_argument(
|
||||
"--handwritten-input-configs",
|
||||
action="store_true",
|
||||
help="Assume the input configuration fragments are "
|
||||
"handwritten fragments and do additional checks "
|
||||
"on them, like no promptless symbols being "
|
||||
"assigned",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--forced-input-configs",
|
||||
action="store_true",
|
||||
help="Indicate the input configuration files are "
|
||||
"followed by an forced configuration file."
|
||||
"The forced configuration is used to forcefully "
|
||||
"set specific configuration settings to a "
|
||||
"pre-defined value and thereby remove any user "
|
||||
" adjustments.",
|
||||
)
|
||||
parser.add_argument("kconfig_file", help="Top-level Kconfig file")
|
||||
parser.add_argument("config_out", help="Output configuration file")
|
||||
parser.add_argument("header_out", help="Output header file")
|
||||
parser.add_argument(
|
||||
"kconfig_list_out", help="Output file for list of parsed Kconfig files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"configs_in",
|
||||
nargs="+",
|
||||
help="Input configuration fragments. Will be merged " "together.",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def warn(msg):
|
||||
# Use a large fill() width to try to avoid linebreaks in the symbol
|
||||
# reference link, and add some extra newlines to set the message off from
|
||||
# surrounding text (this usually gets printed as part of spammy CMake
|
||||
# output)
|
||||
print("\n" + textwrap.fill("warning: " + msg, 100) + "\n", file=sys.stderr)
|
||||
|
||||
|
||||
def err(msg):
|
||||
sys.exit("\n" + textwrap.fill("error: " + msg, 100) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
60
managed_components/lvgl__lvgl/scripts/kconfig_verify.py
Normal file
60
managed_components/lvgl__lvgl/scripts/kconfig_verify.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
import kconfiglib
|
||||
except ImportError:
|
||||
print("Need kconfiglib package, do `pip3 install kconfiglib`")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def verify_kconfig(kconfig_file):
|
||||
kconf = kconfiglib.Kconfig(kconfig_file)
|
||||
|
||||
if kconf.warnings:
|
||||
print("Warnings found:")
|
||||
for warning in kconf.warnings:
|
||||
print(warning)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("No warnings found.")
|
||||
|
||||
|
||||
def check_kconfig_spaces(file_path):
|
||||
"""Check for space-based indentation in a Kconfig file"""
|
||||
try:
|
||||
# Read file content to check for spaces
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
space_indent_lines = []
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
# Check for leading spaces (skip empty lines)
|
||||
stripped_line = line.lstrip()
|
||||
if stripped_line and len(line) > len(stripped_line):
|
||||
# Extract the indentation part
|
||||
indent = line[: -len(stripped_line)] if stripped_line else line
|
||||
if " " in indent:
|
||||
space_indent_lines.append((line_num, indent))
|
||||
|
||||
if space_indent_lines:
|
||||
print(f"Space-based indentation found in file {file_path}:")
|
||||
for line_num, indent in space_indent_lines:
|
||||
print(f"Line {line_num}: Indent contains {indent.count(' ')} spaces")
|
||||
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"No space-based indentation found in file {file_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing file {file_path}: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: python {sys.argv[0]} <Kconfig_file>")
|
||||
sys.exit(1)
|
||||
|
||||
verify_kconfig(sys.argv[1])
|
||||
check_kconfig_spaces(sys.argv[1])
|
||||
349
managed_components/lvgl__lvgl/scripts/lv_conf_internal_gen.py
Normal file
349
managed_components/lvgl__lvgl/scripts/lv_conf_internal_gen.py
Normal file
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''
|
||||
Generates lv_conf_internal.h from lv_conf_template.h to provide default values
|
||||
'''
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(__file__)
|
||||
LV_CONF_TEMPLATE = os.path.join(SCRIPT_DIR, "..", "lv_conf_template.h")
|
||||
LV_CONF_INTERNAL = os.path.join(SCRIPT_DIR, "..", "src", "lv_conf_internal.h")
|
||||
|
||||
if sys.version_info < (3,6,0):
|
||||
print("Python >=3.6 is required", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
def check_for_tabs(file_path):
|
||||
errors = []
|
||||
with open(file_path, 'r') as file:
|
||||
for line_number, line in enumerate(file, 1):
|
||||
if '\t' in line:
|
||||
errors.append(f" {file_path}:{line_number}")
|
||||
|
||||
if errors:
|
||||
print(f"Tabs found in the following files:", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(error, file=sys.stderr)
|
||||
print("Please replace tabs with spaces.", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
check_for_tabs(LV_CONF_TEMPLATE)
|
||||
|
||||
fin = open(LV_CONF_TEMPLATE)
|
||||
fout = open(LV_CONF_INTERNAL, "w", newline='')
|
||||
|
||||
fout.write(
|
||||
'''/**
|
||||
* GENERATED FILE, DO NOT EDIT IT!
|
||||
* @file lv_conf_internal.h
|
||||
* This file ensures all defines of lv_conf.h have a default value.
|
||||
*/
|
||||
|
||||
#ifndef LV_CONF_INTERNAL_H
|
||||
#define LV_CONF_INTERNAL_H
|
||||
/* clang-format off */
|
||||
|
||||
/* Config options */
|
||||
#define LV_OS_NONE 0
|
||||
#define LV_OS_PTHREAD 1
|
||||
#define LV_OS_FREERTOS 2
|
||||
#define LV_OS_CMSIS_RTOS2 3
|
||||
#define LV_OS_RTTHREAD 4
|
||||
#define LV_OS_WINDOWS 5
|
||||
#define LV_OS_MQX 6
|
||||
#define LV_OS_SDL2 7
|
||||
#define LV_OS_CUSTOM 255
|
||||
|
||||
#define LV_STDLIB_BUILTIN 0
|
||||
#define LV_STDLIB_CLIB 1
|
||||
#define LV_STDLIB_MICROPYTHON 2
|
||||
#define LV_STDLIB_RTTHREAD 3
|
||||
#define LV_STDLIB_CUSTOM 255
|
||||
|
||||
#define LV_DRAW_SW_ASM_NONE 0
|
||||
#define LV_DRAW_SW_ASM_NEON 1
|
||||
#define LV_DRAW_SW_ASM_HELIUM 2
|
||||
#define LV_DRAW_SW_ASM_RISCV_V 3
|
||||
#define LV_DRAW_SW_ASM_CUSTOM 255
|
||||
|
||||
#define LV_NEMA_LIB_NONE 0
|
||||
#define LV_NEMA_LIB_M33_REVC 1
|
||||
#define LV_NEMA_LIB_M33_NEMAPVG 2
|
||||
#define LV_NEMA_LIB_M55 3
|
||||
#define LV_NEMA_LIB_M7 4
|
||||
|
||||
#define LV_NEMA_HAL_CUSTOM 0
|
||||
#define LV_NEMA_HAL_STM32 1
|
||||
|
||||
#define LV_NANOVG_BACKEND_GL2 1
|
||||
#define LV_NANOVG_BACKEND_GL3 2
|
||||
#define LV_NANOVG_BACKEND_GLES2 3
|
||||
#define LV_NANOVG_BACKEND_GLES3 4
|
||||
|
||||
/** Handle special Kconfig options. */
|
||||
#ifndef LV_KCONFIG_IGNORE
|
||||
#include "lv_conf_kconfig.h"
|
||||
#if defined(CONFIG_LV_CONF_SKIP) && !defined(LV_CONF_SKIP)
|
||||
#define LV_CONF_SKIP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* If "lv_conf.h" is available from here try to use it later. */
|
||||
#ifdef __has_include
|
||||
#if __has_include("lv_conf.h")
|
||||
#ifndef LV_CONF_INCLUDE_SIMPLE
|
||||
#define LV_CONF_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* If lv_conf.h is not skipped, include it. */
|
||||
#if !defined(LV_CONF_SKIP) || defined(LV_CONF_PATH)
|
||||
#ifdef LV_CONF_PATH /* If there is a path defined for lv_conf.h, use it */
|
||||
#include LV_CONF_PATH /* Note: Make sure to define custom CONF_PATH as a string */
|
||||
#elif defined(LV_CONF_INCLUDE_SIMPLE) /* Or simply include lv_conf.h is enabled. */
|
||||
#include "lv_conf.h"
|
||||
#else
|
||||
#include "../../lv_conf.h" /* Else assume lv_conf.h is next to the lvgl folder. */
|
||||
#endif
|
||||
#if !defined(LV_CONF_H) && !defined(LV_CONF_SUPPRESS_DEFINE_CHECK)
|
||||
/* #include will sometimes silently fail when __has_include is used */
|
||||
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80753 */
|
||||
#pragma message("Possible failure to include lv_conf.h, please read the comment in this file if you get errors")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LV_COLOR_DEPTH
|
||||
#define LV_KCONFIG_PRESENT
|
||||
#endif
|
||||
|
||||
/*----------------------------------
|
||||
* Start parsing lv_conf_template.h
|
||||
-----------------------------------*/
|
||||
'''
|
||||
)
|
||||
|
||||
started = 0
|
||||
|
||||
for line in fin.read().splitlines():
|
||||
if not started:
|
||||
if '#define LV_CONF_H' in line:
|
||||
started = 1
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
if '/*--END OF LV_CONF_H--*/' in line: break
|
||||
|
||||
#Is there a #define in this line?
|
||||
r = re.search(r'^([\s]*)#[\s]*(undef|define)[\s]+([^\s]+).*$', line) # \s means any white space character
|
||||
|
||||
if r:
|
||||
indent = r[1]
|
||||
|
||||
name = r[3]
|
||||
name = re.sub(r'\(.*?\)', '', name, count=1) #remove parentheses from macros. E.g. MY_FUNC(5) -> MY_FUNC
|
||||
|
||||
line = re.sub(r'[\s]*', '', line, count=1)
|
||||
|
||||
#If the value should be 1 (enabled) by default use a more complex structure for Kconfig checks because
|
||||
#if a not defined CONFIG_... value should be interpreted as 0 and not the LVGL default
|
||||
is_one = re.search(r'#[\s]*define[\s]*[A-Z0-9_]+[\s]+1([\s]*$|[\s]+)', line)
|
||||
if is_one:
|
||||
#1. Use the value if already set from lv_conf.h or anything else (i.e. do nothing)
|
||||
#2. In Kconfig environment use the CONFIG_... value if set, else use 0
|
||||
#3. In not Kconfig environment use the LVGL's default value
|
||||
|
||||
fout.write(
|
||||
f'{indent}#ifndef {name}\n'
|
||||
f'{indent} #ifdef LV_KCONFIG_PRESENT\n'
|
||||
f'{indent} #ifdef CONFIG_{name.upper()}\n'
|
||||
f'{indent} #define {name} CONFIG_{name.upper()}\n'
|
||||
f'{indent} #else\n'
|
||||
f'{indent} #define {name} 0\n'
|
||||
f'{indent} #endif\n'
|
||||
f'{indent} #else\n'
|
||||
f'{indent} {line}\n'
|
||||
f'{indent} #endif\n'
|
||||
f'{indent}#endif\n'
|
||||
)
|
||||
else:
|
||||
#1. Use the value if already set from lv_conf.h or anything else (i.e. do nothing)
|
||||
#2. Use the Kconfig value if set
|
||||
#3. Use the LVGL's default value
|
||||
|
||||
fout.write(
|
||||
f'{indent}#ifndef {name}\n'
|
||||
f'{indent} #ifdef CONFIG_{name.upper()}\n'
|
||||
f'{indent} #define {name} CONFIG_{name.upper()}\n'
|
||||
f'{indent} #else\n'
|
||||
f'{indent} {line}\n'
|
||||
f'{indent} #endif\n'
|
||||
f'{indent}#endif\n'
|
||||
)
|
||||
|
||||
elif re.search('^ *typedef .*;.*$', line):
|
||||
continue #ignore typedefs to avoid redeclaration
|
||||
else:
|
||||
fout.write(f'{line}\n')
|
||||
|
||||
fout.write(
|
||||
r'''
|
||||
|
||||
/*----------------------------------
|
||||
* End of parsing lv_conf_template.h
|
||||
-----------------------------------*/
|
||||
|
||||
/*Fix inconsistent name*/
|
||||
#define LV_USE_ANIMIMAGE LV_USE_ANIMIMG
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
LV_EXPORT_CONST_INT(LV_DPI_DEF);
|
||||
LV_EXPORT_CONST_INT(LV_DRAW_BUF_STRIDE_ALIGN);
|
||||
LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN);
|
||||
#endif
|
||||
|
||||
#undef LV_KCONFIG_PRESENT
|
||||
|
||||
/* Disable VGLite drivers if VGLite drawing is disabled */
|
||||
#ifndef LV_USE_VG_LITE_DRIVER
|
||||
#define LV_USE_VG_LITE_DRIVER 0
|
||||
#endif
|
||||
|
||||
#ifndef LV_USE_VG_LITE_THORVG
|
||||
#define LV_USE_VG_LITE_THORVG 0
|
||||
#endif
|
||||
|
||||
/* Set some defines if a dependency is disabled. */
|
||||
#if LV_USE_LOG == 0
|
||||
#define LV_LOG_LEVEL LV_LOG_LEVEL_NONE
|
||||
#define LV_LOG_TRACE_MEM 0
|
||||
#define LV_LOG_TRACE_TIMER 0
|
||||
#define LV_LOG_TRACE_INDEV 0
|
||||
#define LV_LOG_TRACE_DISP_REFR 0
|
||||
#define LV_LOG_TRACE_EVENT 0
|
||||
#define LV_LOG_TRACE_OBJ_CREATE 0
|
||||
#define LV_LOG_TRACE_LAYOUT 0
|
||||
#define LV_LOG_TRACE_ANIM 0
|
||||
#endif /*LV_USE_LOG*/
|
||||
|
||||
#if LV_USE_WAYLAND
|
||||
/*Automatically detect wayland backend*/
|
||||
#if LV_USE_OPENGLES
|
||||
#define LV_WAYLAND_USE_EGL 1
|
||||
#define LV_WAYLAND_USE_G2D 0
|
||||
#define LV_WAYLAND_USE_SHM 0
|
||||
#elif LV_USE_G2D
|
||||
#define LV_WAYLAND_USE_EGL 0
|
||||
#define LV_WAYLAND_USE_G2D 1
|
||||
#define LV_WAYLAND_USE_SHM 0
|
||||
#else
|
||||
#define LV_WAYLAND_USE_EGL 0
|
||||
#define LV_WAYLAND_USE_G2D 0
|
||||
#define LV_WAYLAND_USE_SHM 1
|
||||
#endif
|
||||
#else
|
||||
#define LV_WAYLAND_USE_G2D 0
|
||||
#define LV_WAYLAND_USE_SHM 0
|
||||
#define LV_WAYLAND_USE_EGL 0
|
||||
#endif
|
||||
|
||||
#if LV_USE_LINUX_DRM
|
||||
#if LV_USE_OPENGLES
|
||||
#define LV_LINUX_DRM_USE_EGL 1
|
||||
#else
|
||||
#define LV_LINUX_DRM_USE_EGL 0
|
||||
#endif /* LV_USE_OPENGLES */
|
||||
#else
|
||||
#define LV_LINUX_DRM_USE_EGL 0
|
||||
#endif /*LV_USE_LINUX_DRM*/
|
||||
|
||||
#if LV_USE_SYSMON == 0
|
||||
#define LV_USE_PERF_MONITOR 0
|
||||
#define LV_USE_MEM_MONITOR 0
|
||||
#define LV_SYSMON_PROC_IDLE_AVAILABLE 0
|
||||
#endif /*LV_USE_SYSMON*/
|
||||
|
||||
#if LV_USE_PERF_MONITOR == 0
|
||||
#define LV_USE_PERF_MONITOR_LOG_MODE 0
|
||||
#endif /*LV_USE_PERF_MONITOR*/
|
||||
|
||||
#if LV_BUILD_DEMOS == 0
|
||||
#define LV_USE_DEMO_WIDGETS 0
|
||||
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
|
||||
#define LV_USE_DEMO_BENCHMARK 0
|
||||
#define LV_USE_DEMO_RENDER 0
|
||||
#define LV_USE_DEMO_STRESS 0
|
||||
#define LV_USE_DEMO_MUSIC 0
|
||||
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
|
||||
#define LV_USE_DEMO_FLEX_LAYOUT 0
|
||||
#define LV_USE_DEMO_MULTILANG 0
|
||||
#define LV_USE_DEMO_EBIKE 0
|
||||
#define LV_USE_DEMO_HIGH_RES 0
|
||||
#define LV_USE_DEMO_SMARTWATCH 0
|
||||
#define LV_USE_DEMO_GLTF 0
|
||||
#endif /* LV_BUILD_DEMOS */
|
||||
|
||||
#ifndef LV_USE_LZ4
|
||||
#if (LV_USE_LZ4_INTERNAL || LV_USE_LZ4_EXTERNAL)
|
||||
#define LV_USE_LZ4 1
|
||||
#else
|
||||
#define LV_USE_LZ4 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LV_USE_THORVG
|
||||
#if (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
|
||||
#define LV_USE_THORVG 1
|
||||
#else
|
||||
#define LV_USE_THORVG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LV_USE_SDL && LV_USE_OPENGLES && (LV_USE_DRAW_OPENGLES || LV_USE_DRAW_NANOVG)
|
||||
#define LV_SDL_USE_EGL 1
|
||||
#else
|
||||
#define LV_SDL_USE_EGL 0
|
||||
#endif
|
||||
|
||||
#ifndef LV_USE_EGL
|
||||
#if LV_LINUX_DRM_USE_EGL || LV_WAYLAND_USE_EGL || LV_SDL_USE_EGL
|
||||
#define LV_USE_EGL 1
|
||||
#else
|
||||
#define LV_USE_EGL 0
|
||||
#endif
|
||||
#endif /* LV_USE_EGL */
|
||||
|
||||
|
||||
#if LV_USE_OS
|
||||
#if (LV_USE_FREETYPE || LV_USE_THORVG) && LV_DRAW_THREAD_STACK_SIZE < (32 * 1024)
|
||||
#error "Increase LV_DRAW_THREAD_STACK_SIZE to at least 32KB for FreeType or ThorVG."
|
||||
#endif
|
||||
|
||||
#if defined(LV_DRAW_THREAD_STACKSIZE) && !defined(LV_DRAW_THREAD_STACK_SIZE)
|
||||
#warning "LV_DRAW_THREAD_STACKSIZE was renamed to LV_DRAW_THREAD_STACK_SIZE. Please update lv_conf.h or run menuconfig again."
|
||||
#define LV_DRAW_THREAD_STACK_SIZE LV_DRAW_THREAD_STACKSIZE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*Allow only upper case letters and '/' ('/' is a special case for backward compatibility)*/
|
||||
#define LV_FS_IS_VALID_LETTER(l) ((l) == '/' || ((l) >= 'A' && (l) <= 'Z'))
|
||||
|
||||
/* If running without lv_conf.h, add typedefs with default value. */
|
||||
#ifdef LV_CONF_SKIP
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /*Disable warnings for Visual Studio*/
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif /*defined(LV_CONF_SKIP)*/
|
||||
|
||||
#endif /*LV_CONF_INTERNAL_H*/
|
||||
'''
|
||||
)
|
||||
|
||||
fin.close()
|
||||
fout.close()
|
||||
|
||||
check_for_tabs(LV_CONF_INTERNAL)
|
||||
190
managed_components/lvgl__lvgl/scripts/lv_templ_check.py
Normal file
190
managed_components/lvgl__lvgl/scripts/lv_templ_check.py
Normal file
@@ -0,0 +1,190 @@
|
||||
import re
|
||||
import argparse
|
||||
import os
|
||||
import traceback
|
||||
|
||||
ignored_file_prefixes = {
|
||||
"lv_conf_template.h",
|
||||
"lv_conf_cmsis.h",
|
||||
"lv_test_perf_conf.h",
|
||||
"lv_font_"
|
||||
}
|
||||
|
||||
ignored_top_dirs = {
|
||||
".git",
|
||||
"docs"
|
||||
}
|
||||
|
||||
h_filename_pattern = re.compile(r"lv_[\w.]*\.h")
|
||||
h_pattern = re.compile(
|
||||
r"\s*/\*\*.+?"
|
||||
r"@file +(\S*).+?"
|
||||
r"\*/\s+?#ifndef +(\w+_H)\s+#define +(\w+_H).+"
|
||||
r"#endif */\* *([^*]+?) *\*/\s*",
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
c_filename_pattern = re.compile(r"lv_[\w.]*\.c")
|
||||
c_pattern = re.compile(
|
||||
r"\s*/\*\*.+?"
|
||||
r"@file +(\S*)",
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
file_comment_help_f = """\
|
||||
'{0}' should have at the top:
|
||||
/**
|
||||
* @file {1}
|
||||
*
|
||||
*/
|
||||
instead, has:
|
||||
/**
|
||||
* @file {2}
|
||||
*
|
||||
*/
|
||||
"""
|
||||
|
||||
guard_help_f = """\
|
||||
'{0}' should have include guards like:
|
||||
#ifndef {1}
|
||||
#define {1}
|
||||
...
|
||||
#endif /*{1}*/
|
||||
instead, has:
|
||||
#ifndef {2}
|
||||
#define {3}
|
||||
...
|
||||
#endif /*{4}*/
|
||||
"""
|
||||
|
||||
# regex, arg parsing and other stuff skipped here.
|
||||
|
||||
def ignore_file(basename):
|
||||
result = False
|
||||
|
||||
for pattern in ignored_file_prefixes:
|
||||
if basename.startswith(pattern):
|
||||
result = True
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def ignore_dir(path):
|
||||
result = False
|
||||
|
||||
for top_dir in ignored_top_dirs:
|
||||
sub_path = os.path.join('lvgl', top_dir)
|
||||
if sub_path in path:
|
||||
result = True
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
def debug(*args_, **kwargs):
|
||||
if args.verbose:
|
||||
print(*args_, **kwargs)
|
||||
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument("--fix", action="store_true", help="fix incorrect files")
|
||||
arg_parser.add_argument("-q", "--quiet", action="store_true", help="don't print mismatch info")
|
||||
arg_parser.add_argument("-v", "--verbose", action="store_true", help="print debug info")
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
cfg_project_dir = '..'
|
||||
base_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
project_dir = os.path.abspath(os.path.join(base_dir, cfg_project_dir))
|
||||
debug(f'ignore dirs [{ignored_top_dirs}]')
|
||||
|
||||
ok = True
|
||||
|
||||
for root, dirs, basenames in os.walk(project_dir):
|
||||
if ignore_dir(root):
|
||||
continue
|
||||
|
||||
debug(f'root [{root}]')
|
||||
for basename in basenames:
|
||||
if ignore_file(basename):
|
||||
continue
|
||||
|
||||
file_ok = True
|
||||
a_file_was_processed = False
|
||||
path = os.path.join(root, basename)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# H File?
|
||||
# -------------------------------------------------------------
|
||||
if h_filename_pattern.fullmatch(basename):
|
||||
a_file_was_processed = True
|
||||
debug(f'Opening H file [{path}]')
|
||||
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
cont = f.read().decode('utf-8')
|
||||
except:
|
||||
print(f'Error attempting UTF-8 decode on [{path}].')
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
||||
m = h_pattern.fullmatch(cont)
|
||||
if m is not None:
|
||||
debug(' Match.')
|
||||
if m[1] != basename:
|
||||
file_ok = False
|
||||
if not args.quiet:
|
||||
print(file_comment_help_f.format(path, basename, m[1]))
|
||||
|
||||
guard_name = f"LV_{basename[3:-2].upper()}_H"
|
||||
if any(m[i] != guard_name for i in range(2, 4+1)):
|
||||
file_ok = False
|
||||
if not args.quiet:
|
||||
print(guard_help_f.format(path, guard_name, m[2], m[3], m[4]))
|
||||
|
||||
replacements = (basename, guard_name, guard_name, guard_name)
|
||||
else:
|
||||
debug(f' No match with regex [{h_pattern.pattern}].')
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# C File?
|
||||
# -------------------------------------------------------------
|
||||
elif c_filename_pattern.fullmatch(basename):
|
||||
a_file_was_processed = True
|
||||
debug(f'Opening C file [{path}]')
|
||||
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
cont = f.read().decode('utf-8')
|
||||
except:
|
||||
print(f'Error attempting UTF-8 decode on [{path}].')
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
||||
m = c_pattern.match(cont)
|
||||
if m is not None:
|
||||
debug(' Match.')
|
||||
if m[1] != basename:
|
||||
file_ok = False
|
||||
if not args.quiet:
|
||||
print(file_comment_help_f.format(path, basename, m[1]))
|
||||
|
||||
replacements = (basename, )
|
||||
else:
|
||||
debug(f' No match with regex [{c_pattern.pattern}].')
|
||||
|
||||
if a_file_was_processed:
|
||||
if not file_ok:
|
||||
debug(f'NOT OK [{path}]')
|
||||
ok = False
|
||||
if args.fix:
|
||||
for i in reversed(range(0, len(replacements))):
|
||||
span = m.regs[i + 1]
|
||||
repl = replacements[i]
|
||||
cont = cont[:span[0]] + repl + cont[span[1]:]
|
||||
with open(path, "w") as f:
|
||||
f.write(cont)
|
||||
else:
|
||||
debug(f'OK [{path}]')
|
||||
|
||||
|
||||
if not ok:
|
||||
exit(1)
|
||||
@@ -0,0 +1,237 @@
|
||||
"""
|
||||
This script takes json and mpk input files and creates a PR comment in markdown format.
|
||||
|
||||
An mpk (msgpack) file contains the benchmark result history for a specific config.
|
||||
Unpacked, this file will have the following format:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"commit_hash": "<commit_hash>"
|
||||
"scenes": [
|
||||
{
|
||||
"scene_name": "",
|
||||
"avg_cpu": 0,
|
||||
"avg_fps": 0,
|
||||
"avg_time": 0,
|
||||
"render_time": 0,
|
||||
"flush_time": 0,
|
||||
},
|
||||
...
|
||||
]
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
A json file contains the benchmark result for a specific commit:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"scene_name": "",
|
||||
"avg_cpu": 0,
|
||||
"avg_fps": 0,
|
||||
"avg_time": 0,
|
||||
"render_time": 0,
|
||||
"flush_time": 0,
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
This script reads the previous and new benchmark results, compares them and creates a comment.
|
||||
|
||||
Example comment:
|
||||
|
||||
```
|
||||
Hi :wave:, thank you for your PR!
|
||||
|
||||
We've run benchmarks in an emulated environment. Here are the results:
|
||||
|
||||
#### ARM Emulated 32b - lv_conf_perf32b
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Detailed Results Per Scene
|
||||
</summary>
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| Empty screen | 11 | 25 | 0 | 0 | 0 |
|
||||
| Moving wallpaper | 1 | 25 | 0 | 0 | 0 |
|
||||
| Single rectangle | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple rectangles | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple RGB images | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple ARGB images | 22 (-1)| 25 | 1 | 1 | 0 |
|
||||
| Rotated ARGB images | 47 (-1)| 24 | 20 | 20 | 0 |
|
||||
| Multiple labels | 2 (-2)| 25 | 0 | 0 | 0 |
|
||||
| Screen sized text | 30 (+1)| 24 (-1)| 11 (-1)| 11 (-1)| 0 |
|
||||
| Multiple arcs | 19 (+4)| 24 | 7 | 7 | 0 |
|
||||
| Containers | 1 (-1)| 25 | 0 | 0 | 0 |
|
||||
| Containers with overlay | 87 (-2)| 21 | 44 | 44 | 0 |
|
||||
| Containers with opa | 23 (+1)| 25 | 4 | 4 | 0 |
|
||||
| Containers with opa_layer | 22 (+1)| 25 | 8 | 8 | 0 |
|
||||
| Containers with scrolling | 25 | 25 | 10 | 10 | 0 |
|
||||
| Widgets demo | 34 | 24 (-1)| 13 | 13 | 0 |
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Disclaimer: These benchmarks were run in an emulated environment using QEMU with instruction counting mode.
|
||||
The timing values represent relative performance metrics within this specific virtualized setup and should
|
||||
not be interpreted as absolute real-world performance measurements. Values are deterministic and useful for
|
||||
comparing different LVGL features and configurations, but may not correlate directly with performance on
|
||||
physical hardware. The measurements are intended for comparative analysis only.
|
||||
|
||||
|
||||
---
|
||||
|
||||
:robot: This comment was automatically generated by a bot.
|
||||
```
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import msgpack
|
||||
|
||||
|
||||
DISCLAIMER = """
|
||||
Disclaimer: These benchmarks were run in an emulated environment using QEMU with instruction counting mode.
|
||||
The timing values represent relative performance metrics within this specific virtualized setup and should
|
||||
not be interpreted as absolute real-world performance measurements. Values are deterministic and useful for
|
||||
comparing different LVGL features and configurations, but may not correlate directly with performance on
|
||||
physical hardware. The measurements are intended for comparative analysis only.
|
||||
"""
|
||||
|
||||
|
||||
def format_table(results: list[dict], prev_results: list[dict]):
|
||||
|
||||
table = "| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |\n"
|
||||
table += "|------------|------------|---------|--------------|----------------|--------------|\n"
|
||||
data_keys = ["avg_cpu", "avg_fps", "avg_time", "render_time", "flush_time"]
|
||||
|
||||
for scene_results, prev_scene_results in zip(results, prev_results):
|
||||
delta_p = {
|
||||
key: ((scene_results[key] - prev_scene_results[key])) for key in data_keys
|
||||
}
|
||||
table += f"| {scene_results['scene_name']} |"
|
||||
for key in data_keys:
|
||||
if delta_p[key] == 0:
|
||||
table += f" {scene_results[key]} |"
|
||||
else:
|
||||
table += f" {scene_results[key]} ({delta_p[key]:+})|"
|
||||
table += "\n"
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Process previous and new results, and output a comment file."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--previous",
|
||||
type=str,
|
||||
nargs="+",
|
||||
required=False,
|
||||
help="Path to the previous results file (supports multiple, e.g., results*.mpk)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--new",
|
||||
type=str,
|
||||
nargs="+",
|
||||
required=True,
|
||||
help="Paths to new results files (supports multiple, e.g., results*.json)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Output file path (e.g., comment.md)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
previous_results_paths = args.previous
|
||||
results_paths = args.new
|
||||
output_path = args.output
|
||||
|
||||
previous_results_map: dict[str, list[dict]] = {}
|
||||
if previous_results_paths:
|
||||
for results_path in previous_results_paths:
|
||||
_, image_type, config = results_path.replace(".mpk", "").split("-")
|
||||
|
||||
with open(results_path, "rb") as f:
|
||||
previousb = f.read()
|
||||
rs: list = msgpack.unpackb(previousb)
|
||||
# We store the filename so it's easier to match with the related results
|
||||
previous_results_map[os.path.basename(results_path)] = rs
|
||||
|
||||
new_results: dict[str, list[dict]] = {}
|
||||
for results_path in results_paths:
|
||||
with open(results_path, "r") as f:
|
||||
r: list[dict] = json.load(f)
|
||||
# We store the filename so it's easier to match with the related results
|
||||
new_results[os.path.basename(results_path)] = r
|
||||
|
||||
comment = "Hi :wave:, thank you for your PR!\n\n"
|
||||
comment += "We've run benchmarks in an emulated environment."
|
||||
comment += " Here are the results:\n\n"
|
||||
|
||||
for result_path, result in new_results.items():
|
||||
mpk_path = result_path.replace(".json", ".mpk")
|
||||
|
||||
new_all_scene_avg = [
|
||||
scene for scene in result if scene["scene_name"] == "All scenes avg."
|
||||
]
|
||||
|
||||
prev_results = previous_results_map.get(mpk_path, [])
|
||||
|
||||
if len(prev_results) > 0:
|
||||
prev_scenes = prev_results[-1]["scenes"]
|
||||
prev_all_scene_avg = [
|
||||
[
|
||||
scene
|
||||
for scene in prev_scenes
|
||||
if scene["scene_name"] == "All scenes avg."
|
||||
][0]
|
||||
]
|
||||
prev_results = prev_scenes
|
||||
else:
|
||||
# If there are no previous results, we use the current result as
|
||||
# the previous aswell
|
||||
# In this case, the difference will always be zero and we won't
|
||||
# add any new information to the result table
|
||||
prev_results = result
|
||||
prev_all_scene_avg = new_all_scene_avg
|
||||
|
||||
_, image_type, config = result_path.replace(".json", "").split("-")
|
||||
comment += f"#### ARM Emulated {image_type} - {config}\n\n"
|
||||
comment += format_table(new_all_scene_avg, prev_all_scene_avg)
|
||||
comment += "\n<details>"
|
||||
comment += "\n<summary>"
|
||||
comment += "\nDetailed Results Per Scene"
|
||||
comment += "\n</summary>\n\n"
|
||||
comment += format_table(result, prev_results)
|
||||
comment += "\n\n</details>\n\n"
|
||||
|
||||
comment += DISCLAIMER
|
||||
comment += "\n\n"
|
||||
comment += "---"
|
||||
comment += "\n\n"
|
||||
comment += ":robot: This comment was automatically generated by a bot."
|
||||
|
||||
with open(output_path, "w") as f:
|
||||
f.write(comment)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
This script parses a docker logs file for the lv_benchmark summary results
|
||||
|
||||
The input looks something like:
|
||||
```
|
||||
{"log":"SO3: starting the initial process ...\r\n","stream":"stdout","time":"2025-03-14T20:32:17.712134853Z"}
|
||||
{"log":"\r\n","stream":"stdout","time":"2025-03-14T20:32:17.712146014Z"}
|
||||
{"log":"\r\n","stream":"stdout","time":"2025-03-14T20:32:17.712150624Z"}
|
||||
{"log":"Starting LVGL Benchmark\r\n","stream":"stdout","time":"2025-03-14T20:32:17.816299617Z"}
|
||||
{"log":"Warning: The guest is now late by 0.0 to 1.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:32:19.129030996Z"}
|
||||
{"log":"Warning: The guest is now late by 1.0 to 2.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:32:40.699491404Z"}
|
||||
{"log":"Warning: The guest is now late by 2.0 to 3.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:33:02.966355127Z"}
|
||||
{"log":"Warning: The guest is now late by 3.0 to 4.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:33:30.06251689Z"}
|
||||
{"log":"Warning: The guest is now late by 4.0 to 5.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:33:38.253535255Z"}
|
||||
{"log":"Warning: The guest is now late by 5.0 to 6.0 seconds\r\n","stream":"stdout","time":"2025-03-14T20:33:51.010250882Z"}
|
||||
{"log":"Benchmark Summary (9.3.0 dev)\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.855785347Z"}
|
||||
{"log":"Name, Avg. CPU, Avg. FPS, Avg. time, render time, flush time\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.855997881Z"}
|
||||
{"log":"Empty screen, 11%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.857828373Z"}
|
||||
{"log":"Moving wallpaper, 2%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.858455924Z"}
|
||||
{"log":"Single rectangle, 0%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.859125276Z"}
|
||||
{"log":"Multiple rectangles, 0%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.859847899Z"}
|
||||
{"log":"Multiple RGB images, 0%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.860658884Z"}
|
||||
{"log":"Multiple ARGB images, 23%, 25, 1, 1, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.86156999Z"}
|
||||
{"log":"Rotated ARGB images, 48%, 24, 20, 20, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.862511197Z"}
|
||||
{"log":"Multiple labels, 3%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.863549175Z"}
|
||||
{"log":"Screen sized text, 30%, 24, 11, 11, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.864642275Z"}
|
||||
{"log":"Multiple arcs, 18%, 24, 7, 7, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.865801475Z"}
|
||||
{"log":"Containers, 3%, 25, 0, 0, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.867039657Z"}
|
||||
{"log":"Containers with overlay, 88%, 21, 44, 44, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.868379231Z"}
|
||||
{"log":"Containers with opa, 10%, 24, 3, 3, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.869736765Z"}
|
||||
{"log":"Containers with opa_layer, 22%, 24, 8, 8, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.871269383Z"}
|
||||
{"log":"Containers with scrolling, 25%, 25, 10, 10, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.872899952Z"}
|
||||
{"log":"Widgets demo, 34%, 25, 13, 13, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.874447759Z"}
|
||||
{"log":"All scenes avg.,19%, 24, 7, 7, 0\r\r\n","stream":"stdout","time":"2025-03-14T20:33:57.875031879Z"}
|
||||
{"log":"LVGL Benchmark Over\r\n","stream":"stdout","time":"2025-03-14T20:33:57.933458479Z"}
|
||||
````
|
||||
|
||||
Outpus a json file with the format:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"scene_name": "",
|
||||
"avg_cpu": 0,
|
||||
"avg_fps": 0,
|
||||
"avg_time": 0,
|
||||
"render_time": 0,
|
||||
"flush_time": 0,
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Missing output path")
|
||||
return
|
||||
|
||||
output_path = sys.argv[1]
|
||||
found_start_of_results = False
|
||||
found_header = False
|
||||
results = []
|
||||
for line in sys.stdin:
|
||||
if "Benchmark Summary" in line:
|
||||
found_start_of_results = True
|
||||
continue
|
||||
if not found_start_of_results:
|
||||
continue
|
||||
|
||||
fmt_line: str = json.loads(line)["log"].strip()
|
||||
cols = fmt_line.split(",")
|
||||
if len(cols) < 6:
|
||||
if fmt_line != "LVGL Benchmark Over":
|
||||
print(f"Warning found invalid log line '{fmt_line}'. Skipping...")
|
||||
continue
|
||||
|
||||
if not found_header:
|
||||
found_header = True
|
||||
continue
|
||||
|
||||
results.append(
|
||||
{
|
||||
"scene_name": cols[0],
|
||||
"avg_cpu": int(cols[1].replace("%", "")),
|
||||
"avg_fps": int(cols[2]),
|
||||
"avg_time": int(cols[3]),
|
||||
"render_time": int(cols[4]),
|
||||
"flush_time": int(cols[5]),
|
||||
}
|
||||
)
|
||||
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(results, f)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script installs extra dependencies for the lvperf Docker images used in the CI pipeline.
|
||||
# The images are based on Alpine Linux and support runtime installation of dependencies,
|
||||
# allowing you to extend functionality without rebuilding the image.
|
||||
#
|
||||
# For guidance on how dependencies are typically added, refer to the Dockerfiles:
|
||||
# - https://github.com/smartobjectoriented/so3/blob/main/docker/Dockerfile.lvperf_32b
|
||||
# - https://github.com/smartobjectoriented/so3/blob/main/docker/Dockerfile.lvperf_64b
|
||||
151
managed_components/lvgl__lvgl/scripts/perf/serialize_results.py
Normal file
151
managed_components/lvgl__lvgl/scripts/perf/serialize_results.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
This script takes json and mpk input files and combines them
|
||||
|
||||
An mpk (msgpack) file contains the benchmark result history for a specific config.
|
||||
Unpacked, this file will have the following format:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"commit_hash": "<commit_hash>"
|
||||
"scenes": [
|
||||
{
|
||||
"scene_name": "",
|
||||
"avg_cpu": 0,
|
||||
"avg_fps": 0,
|
||||
"avg_time": 0,
|
||||
"render_time": 0,
|
||||
"flush_time": 0,
|
||||
}
|
||||
...
|
||||
]
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
A json file contains the benchmark result for a specific commit:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"scene_name": "",
|
||||
"avg_cpu": 0,
|
||||
"avg_fps": 0,
|
||||
"avg_time": 0,
|
||||
"render_time": 0,
|
||||
"flush_time": 0,
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
This script combines the json files with the mpk files and outputs new mpk files
|
||||
|
||||
The files are expected to follow the format 'results-<image_type>-<config_name>.[mpk|json]'
|
||||
|
||||
There are 3 possibilities when handling these files
|
||||
|
||||
1. The pair (`file1.mpk` `file1.json`) exists: Normal operation, we have a history file and some new results.
|
||||
In this case we take the information inside the `file1.json` and append it to the `file1.mpk` before exporting it
|
||||
|
||||
2. `file1.json` exists but `file1.mpk` doesn't: Happens when a new config and/or image type are added and there's no history yet
|
||||
In this case we create a new `file1.mpk` with a single history entry.
|
||||
|
||||
3. `file1.mpk` exists but `file1.json` doesn't: Happens when an old config isn't tested anymore for any reason.
|
||||
In this case we simpyl copy the old `file1.mpk`
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import msgpack
|
||||
import json
|
||||
|
||||
|
||||
def save_mpk(mpk_path, mpk_data):
|
||||
|
||||
with open(mpk_path, "wb") as f:
|
||||
f.write(mpk_data)
|
||||
|
||||
|
||||
def generate_new_mpk(json_path, output_path, commit_hash):
|
||||
|
||||
with open(json_path, "r") as f:
|
||||
json_data: list = json.load(f)
|
||||
|
||||
mpk_data = msgpack.packb([{"commit_hash": commit_hash, "scenes": json_data}])
|
||||
save_mpk(output_path, mpk_data)
|
||||
|
||||
|
||||
def append_json_to_mpk(mpk_path, json_path, output_path, commit_hash):
|
||||
with open(mpk_path, "rb") as f:
|
||||
mpk_data: list = msgpack.unpackb(f.read())
|
||||
|
||||
with open(json_path, "r") as f:
|
||||
json_data = json.load(f)
|
||||
|
||||
mpk_data.append({"commit_hash": commit_hash, "scenes": json_data})
|
||||
|
||||
save_mpk(output_path, msgpack.packb(mpk_data))
|
||||
|
||||
|
||||
def path(folder, filename):
|
||||
return os.path.join(folder, filename)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Merge new results into previous results"
|
||||
)
|
||||
|
||||
parser.add_argument("--commit-hash", type=str, required=True, help="Commit Hash")
|
||||
parser.add_argument(
|
||||
"--input",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Input Folder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Output Folder",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
input_folder: str = args.input
|
||||
output_folder: str = args.output
|
||||
commit_hash: str = args.commit_hash
|
||||
|
||||
input_files = os.listdir(input_folder)
|
||||
input_mpk: set[str] = {file for file in input_files if ".mpk" in file}
|
||||
input_json: list[str] = [file for file in input_files if ".json" in file]
|
||||
|
||||
for json_file in input_json:
|
||||
mpk_file = json_file.replace(".json", ".mpk")
|
||||
json_path = path(input_folder, json_file)
|
||||
output_path = path(output_folder, mpk_file)
|
||||
|
||||
if mpk_file not in input_mpk:
|
||||
# First time this config is being run
|
||||
print(
|
||||
f"Couldn't find pair for {json_file} - ({mpk_file} not found). Generating new mpk file"
|
||||
)
|
||||
generate_new_mpk(json_path, output_path, commit_hash)
|
||||
else:
|
||||
print(f"Found pair ({mpk_file} {json_file}). Combining")
|
||||
mpk_path = path(input_folder, mpk_file)
|
||||
|
||||
append_json_to_mpk(mpk_path, json_path, output_path, commit_hash)
|
||||
input_mpk.remove(mpk_file)
|
||||
|
||||
# Keep old mpk files
|
||||
for mpk_file in input_mpk:
|
||||
print(f"Couldn't find new reults to add to {mpk_file}. Copying it")
|
||||
mpk_path = path(input_folder, mpk_file)
|
||||
output_path = path(output_folder, mpk_file)
|
||||
shutil.copy2(mpk_path, output_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,83 @@
|
||||
Hi :wave:, thank you for your PR!
|
||||
|
||||
We've run benchmarks in an emulated environment. Here are the results:
|
||||
|
||||
#### ARM Emulated 32b - lv_conf_perf32b
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Detailed Results Per Scene
|
||||
</summary>
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| Empty screen | 11 | 25 | 0 | 0 | 0 |
|
||||
| Moving wallpaper | 1 | 25 | 0 | 0 | 0 |
|
||||
| Single rectangle | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple rectangles | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple RGB images | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple ARGB images | 22 | 25 | 1 | 1 | 0 |
|
||||
| Rotated ARGB images | 47 | 24 | 20 | 20 | 0 |
|
||||
| Multiple labels | 2 | 25 | 0 | 0 | 0 |
|
||||
| Screen sized text | 30 | 24 | 11 | 11 | 0 |
|
||||
| Multiple arcs | 19 | 24 | 7 | 7 | 0 |
|
||||
| Containers | 1 | 25 | 0 | 0 | 0 |
|
||||
| Containers with overlay | 87 | 21 | 44 | 44 | 0 |
|
||||
| Containers with opa | 23 | 25 | 4 | 4 | 0 |
|
||||
| Containers with opa_layer | 22 | 25 | 8 | 8 | 0 |
|
||||
| Containers with scrolling | 25 | 25 | 10 | 10 | 0 |
|
||||
| Widgets demo | 34 | 24 | 13 | 13 | 0 |
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
#### ARM Emulated 64b - lv_conf_perf64b
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| All scenes avg. | 16 | 24 | 6 | 6 | 0 |
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Detailed Results Per Scene
|
||||
</summary>
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| Empty screen | 11 | 25 | 0 | 0 | 0 |
|
||||
| Moving wallpaper | 0 | 25 | 0 | 0 | 0 |
|
||||
| Single rectangle | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple rectangles | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple RGB images | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple ARGB images | 1 | 25 | 0 | 0 | 0 |
|
||||
| Rotated ARGB images | 23 | 25 | 10 | 10 | 0 |
|
||||
| Multiple labels | 3 | 24 | 0 | 0 | 0 |
|
||||
| Screen sized text | 23 | 24 | 10 | 10 | 0 |
|
||||
| Multiple arcs | 15 | 24 | 6 | 6 | 0 |
|
||||
| Containers | 3 | 25 | 0 | 0 | 0 |
|
||||
| Containers with overlay | 90 | 23 | 41 | 41 | 0 |
|
||||
| Containers with opa | 19 | 25 | 4 | 4 | 0 |
|
||||
| Containers with opa_layer | 13 | 24 | 5 | 5 | 0 |
|
||||
| Containers with scrolling | 25 | 25 | 10 | 10 | 0 |
|
||||
| Widgets demo | 33 | 25 | 13 | 13 | 0 |
|
||||
| All scenes avg. | 16 | 24 | 6 | 6 | 0 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Disclaimer: These benchmarks were run in an emulated environment using QEMU with instruction counting mode.
|
||||
The timing values represent relative performance metrics within this specific virtualized setup and should
|
||||
not be interpreted as absolute real-world performance measurements. Values are deterministic and useful for
|
||||
comparing different LVGL features and configurations, but may not correlate directly with performance on
|
||||
physical hardware. The measurements are intended for comparative analysis only.
|
||||
|
||||
|
||||
---
|
||||
|
||||
:robot: This comment was automatically generated by a bot.
|
||||
@@ -0,0 +1 @@
|
||||
[{"scene_name": "Empty screen", "avg_cpu": 11, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Moving wallpaper", "avg_cpu": 1, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Single rectangle", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple rectangles", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple RGB images", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple ARGB images", "avg_cpu": 22, "avg_fps": 25, "avg_time": 1, "render_time": 1, "flush_time": 0}, {"scene_name": "Rotated ARGB images", "avg_cpu": 47, "avg_fps": 24, "avg_time": 20, "render_time": 20, "flush_time": 0}, {"scene_name": "Multiple labels", "avg_cpu": 2, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Screen sized text", "avg_cpu": 30, "avg_fps": 24, "avg_time": 11, "render_time": 11, "flush_time": 0}, {"scene_name": "Multiple arcs", "avg_cpu": 19, "avg_fps": 24, "avg_time": 7, "render_time": 7, "flush_time": 0}, {"scene_name": "Containers", "avg_cpu": 1, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Containers with overlay", "avg_cpu": 87, "avg_fps": 21, "avg_time": 44, "render_time": 44, "flush_time": 0}, {"scene_name": "Containers with opa", "avg_cpu": 23, "avg_fps": 25, "avg_time": 4, "render_time": 4, "flush_time": 0}, {"scene_name": "Containers with opa_layer", "avg_cpu": 22, "avg_fps": 25, "avg_time": 8, "render_time": 8, "flush_time": 0}, {"scene_name": "Containers with scrolling", "avg_cpu": 25, "avg_fps": 25, "avg_time": 10, "render_time": 10, "flush_time": 0}, {"scene_name": "Widgets demo", "avg_cpu": 34, "avg_fps": 24, "avg_time": 13, "render_time": 13, "flush_time": 0}, {"scene_name": "All scenes avg.", "avg_cpu": 20, "avg_fps": 24, "avg_time": 7, "render_time": 7, "flush_time": 0}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"scene_name": "Empty screen", "avg_cpu": 11, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Moving wallpaper", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Single rectangle", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple rectangles", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple RGB images", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple ARGB images", "avg_cpu": 1, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Rotated ARGB images", "avg_cpu": 23, "avg_fps": 25, "avg_time": 10, "render_time": 10, "flush_time": 0}, {"scene_name": "Multiple labels", "avg_cpu": 3, "avg_fps": 24, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Screen sized text", "avg_cpu": 23, "avg_fps": 24, "avg_time": 10, "render_time": 10, "flush_time": 0}, {"scene_name": "Multiple arcs", "avg_cpu": 15, "avg_fps": 24, "avg_time": 6, "render_time": 6, "flush_time": 0}, {"scene_name": "Containers", "avg_cpu": 3, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Containers with overlay", "avg_cpu": 90, "avg_fps": 23, "avg_time": 41, "render_time": 41, "flush_time": 0}, {"scene_name": "Containers with opa", "avg_cpu": 19, "avg_fps": 25, "avg_time": 4, "render_time": 4, "flush_time": 0}, {"scene_name": "Containers with opa_layer", "avg_cpu": 13, "avg_fps": 24, "avg_time": 5, "render_time": 5, "flush_time": 0}, {"scene_name": "Containers with scrolling", "avg_cpu": 25, "avg_fps": 25, "avg_time": 10, "render_time": 10, "flush_time": 0}, {"scene_name": "Widgets demo", "avg_cpu": 33, "avg_fps": 25, "avg_time": 13, "render_time": 13, "flush_time": 0}, {"scene_name": "All scenes avg.", "avg_cpu": 16, "avg_fps": 24, "avg_time": 6, "render_time": 6, "flush_time": 0}]
|
||||
@@ -0,0 +1,83 @@
|
||||
Hi :wave:, thank you for your PR!
|
||||
|
||||
We've run benchmarks in an emulated environment. Here are the results:
|
||||
|
||||
#### ARM Emulated 32b - lv_conf_perf32b
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Detailed Results Per Scene
|
||||
</summary>
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| Empty screen | 11 | 25 | 0 | 0 | 0 |
|
||||
| Moving wallpaper | 1 | 25 | 0 | 0 | 0 |
|
||||
| Single rectangle | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple rectangles | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple RGB images | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple ARGB images | 22 (-1)| 25 | 1 | 1 | 0 |
|
||||
| Rotated ARGB images | 47 (-1)| 24 | 20 | 20 | 0 |
|
||||
| Multiple labels | 2 (-2)| 25 | 0 | 0 | 0 |
|
||||
| Screen sized text | 30 (+1)| 24 (-1)| 11 (-1)| 11 (-1)| 0 |
|
||||
| Multiple arcs | 19 (+4)| 24 | 7 | 7 | 0 |
|
||||
| Containers | 1 (-1)| 25 | 0 | 0 | 0 |
|
||||
| Containers with overlay | 87 (-2)| 21 | 44 | 44 | 0 |
|
||||
| Containers with opa | 23 (+1)| 25 | 4 | 4 | 0 |
|
||||
| Containers with opa_layer | 22 (+1)| 25 | 8 | 8 | 0 |
|
||||
| Containers with scrolling | 25 | 25 | 10 | 10 | 0 |
|
||||
| Widgets demo | 34 | 24 (-1)| 13 | 13 | 0 |
|
||||
| All scenes avg. | 20 | 24 | 7 | 7 | 0 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
#### ARM Emulated 64b - lv_conf_perf64b
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| All scenes avg. | 16 | 24 | 6 | 6 | 0 |
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Detailed Results Per Scene
|
||||
</summary>
|
||||
|
||||
| Scene Name | Avg CPU (%) | Avg FPS | Avg Time (ms) | Render Time (ms) | Flush Time (ms) |
|
||||
|------------|------------|---------|--------------|----------------|--------------|
|
||||
| Empty screen | 11 | 25 | 0 | 0 | 0 |
|
||||
| Moving wallpaper | 0 | 25 | 0 | 0 | 0 |
|
||||
| Single rectangle | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple rectangles | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple RGB images | 0 | 25 | 0 | 0 | 0 |
|
||||
| Multiple ARGB images | 1 | 25 | 0 | 0 | 0 |
|
||||
| Rotated ARGB images | 23 | 25 | 10 | 10 | 0 |
|
||||
| Multiple labels | 3 | 24 | 0 | 0 | 0 |
|
||||
| Screen sized text | 23 | 24 | 10 | 10 | 0 |
|
||||
| Multiple arcs | 15 | 24 | 6 | 6 | 0 |
|
||||
| Containers | 3 (+1)| 25 (+1)| 0 | 0 | 0 |
|
||||
| Containers with overlay | 90 (+2)| 23 | 41 | 41 | 0 |
|
||||
| Containers with opa | 19 | 25 | 4 | 4 | 0 |
|
||||
| Containers with opa_layer | 13 (-2)| 24 (-1)| 5 (-1)| 5 (-1)| 0 |
|
||||
| Containers with scrolling | 25 | 25 (-1)| 10 | 10 | 0 |
|
||||
| Widgets demo | 33 | 25 | 13 | 13 | 0 |
|
||||
| All scenes avg. | 16 | 24 | 6 | 6 | 0 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Disclaimer: These benchmarks were run in an emulated environment using QEMU with instruction counting mode.
|
||||
The timing values represent relative performance metrics within this specific virtualized setup and should
|
||||
not be interpreted as absolute real-world performance measurements. Values are deterministic and useful for
|
||||
comparing different LVGL features and configurations, but may not correlate directly with performance on
|
||||
physical hardware. The measurements are intended for comparative analysis only.
|
||||
|
||||
|
||||
---
|
||||
|
||||
:robot: This comment was automatically generated by a bot.
|
||||
@@ -0,0 +1 @@
|
||||
[{"scene_name": "Empty screen", "avg_cpu": 11, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Moving wallpaper", "avg_cpu": 1, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Single rectangle", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple rectangles", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple RGB images", "avg_cpu": 0, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Multiple ARGB images", "avg_cpu": 22, "avg_fps": 25, "avg_time": 1, "render_time": 1, "flush_time": 0}, {"scene_name": "Rotated ARGB images", "avg_cpu": 47, "avg_fps": 24, "avg_time": 20, "render_time": 20, "flush_time": 0}, {"scene_name": "Multiple labels", "avg_cpu": 2, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Screen sized text", "avg_cpu": 30, "avg_fps": 24, "avg_time": 11, "render_time": 11, "flush_time": 0}, {"scene_name": "Multiple arcs", "avg_cpu": 19, "avg_fps": 24, "avg_time": 7, "render_time": 7, "flush_time": 0}, {"scene_name": "Containers", "avg_cpu": 1, "avg_fps": 25, "avg_time": 0, "render_time": 0, "flush_time": 0}, {"scene_name": "Containers with overlay", "avg_cpu": 87, "avg_fps": 21, "avg_time": 44, "render_time": 44, "flush_time": 0}, {"scene_name": "Containers with opa", "avg_cpu": 23, "avg_fps": 25, "avg_time": 4, "render_time": 4, "flush_time": 0}, {"scene_name": "Containers with opa_layer", "avg_cpu": 22, "avg_fps": 25, "avg_time": 8, "render_time": 8, "flush_time": 0}, {"scene_name": "Containers with scrolling", "avg_cpu": 25, "avg_fps": 25, "avg_time": 10, "render_time": 10, "flush_time": 0}, {"scene_name": "Widgets demo", "avg_cpu": 34, "avg_fps": 24, "avg_time": 13, "render_time": 13, "flush_time": 0}, {"scene_name": "All scenes avg.", "avg_cpu": 20, "avg_fps": 24, "avg_time": 7, "render_time": 7, "flush_time": 0}]
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user