move into firmware subfolder
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2
firmware/.clang-format
Normal file
2
firmware/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BasedOnStyle: Microsoft
|
||||
13
firmware/.devcontainer/Dockerfile
Normal file
13
firmware/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -y && apt-get install udev -y
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
||||
21
firmware/.devcontainer/devcontainer.json
Normal file
21
firmware/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.espIdfPath": "/opt/esp/idf",
|
||||
"idf.toolsPath": "/opt/esp",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
||||
40
firmware/.gitignore
vendored
Normal file
40
firmware/.gitignore
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# esp-idf
|
||||
build/
|
||||
managed_components/
|
||||
sdkconfig
|
||||
sdkconfig.old
|
||||
dependencies.lock
|
||||
config.env
|
||||
kconfigs_projbuild.in
|
||||
kconfigs.in
|
||||
config/
|
||||
|
||||
# cmake
|
||||
cmake-build-debug
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CMakeFiles/
|
||||
CTestTestfile.cmake
|
||||
Makefile
|
||||
CMakeScripts
|
||||
compile_commands.json
|
||||
|
||||
# They ignored Idea (Webstorm) completely
|
||||
.idea/
|
||||
.cache/
|
||||
|
||||
# However, they kept the following 4 files version controlled
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
__pycache__
|
||||
_deps/
|
||||
.cmake/
|
||||
.ninja_*
|
||||
bin/
|
||||
components/**/*.a
|
||||
*.ninja
|
||||
Testing/*
|
||||
15
firmware/.vscode/launch.json
vendored
Normal file
15
firmware/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdbtarget",
|
||||
"request": "attach",
|
||||
"name": "Eclipse CDT GDB Adapter"
|
||||
},
|
||||
{
|
||||
"type": "espidf",
|
||||
"name": "Launch",
|
||||
"request": "launch"
|
||||
}
|
||||
]
|
||||
}
|
||||
106
firmware/CMakeLists.txt
Executable file
106
firmware/CMakeLists.txt
Executable file
@@ -0,0 +1,106 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(system_control)
|
||||
return()
|
||||
else ()
|
||||
set(MAJOR_VERSION 0)
|
||||
set(MINOR_VERSION 0)
|
||||
set(MICRO_VERSION 1)
|
||||
|
||||
project(
|
||||
SystemControl
|
||||
LANGUAGES CXX C
|
||||
VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}"
|
||||
)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS YES)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_INCLUDE_PATH include)
|
||||
set(CMAKE_SOURCE_DIR src)
|
||||
set(CMAKE_MODULE_PATH cmake)
|
||||
|
||||
include(FetchContent)
|
||||
function(include_dependency libName gitURL gitTag)
|
||||
FetchContent_Declare(${libName}
|
||||
GIT_REPOSITORY ${gitURL}
|
||||
GIT_TAG ${gitTag}
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS FALSE
|
||||
)
|
||||
FetchContent_MakeAvailable(${libName})
|
||||
endfunction()
|
||||
|
||||
include_dependency(SDL3 https://github.com/libsdl-org/SDL release-3.2.x)
|
||||
include_dependency(SDL_image https://github.com/libsdl-org/SDL_image release-3.2.x)
|
||||
include_dependency(SDL_ttf https://github.com/libsdl-org/SDL_ttf release-3.2.x)
|
||||
include_dependency(u8g2 https://github.com/olikraus/u8g2 master)
|
||||
|
||||
add_subdirectory(components)
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/Version.h.in"
|
||||
"${CMAKE_SOURCE_DIR}/Version.h"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_INCLUDE_PATH}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${PROJECT_BINARY_DIR}/src
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
WIN32 MACOSX_BUNDLE
|
||||
${CMAKE_SOURCE_DIR}/main.cpp
|
||||
${CMAKE_SOURCE_DIR}/Common.cpp
|
||||
${CMAKE_SOURCE_DIR}/debug/debug_overlay.cpp
|
||||
${CMAKE_SOURCE_DIR}/hal/u8x8_hal_sdl.cpp
|
||||
${CMAKE_SOURCE_DIR}/manager/ResourceManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/model/AppContext.cpp
|
||||
${CMAKE_SOURCE_DIR}/model/Window.cpp
|
||||
${CMAKE_SOURCE_DIR}/ui/Device.cpp
|
||||
${CMAKE_SOURCE_DIR}/ui/UIWidget.cpp
|
||||
${CMAKE_SOURCE_DIR}/ui/widgets/Button.cpp
|
||||
${CMAKE_SOURCE_DIR}/ui/widgets/D_Pad.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
# copy assets to bundle directory
|
||||
set(ASSETS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/assets)
|
||||
set(ASSETS_DEST_DIR $<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/Resources)
|
||||
|
||||
if (EXISTS ${ASSETS_SRC_DIR})
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${ASSETS_SRC_DIR} ${ASSETS_DEST_DIR}
|
||||
COMMENT "Copying assets to bundle via custom command"
|
||||
VERBATIM
|
||||
)
|
||||
else ()
|
||||
message(WARNING "Assets source directory ${ASSETS_SRC_DIR} does not exist. Skipping custom command.")
|
||||
endif ()
|
||||
elseif (WINDOWS)
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${PROJECT_SOURCE_DIR}/assets ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets
|
||||
)
|
||||
endif ()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE SDL_MAIN_USE_CALLBACKS)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
ImGui
|
||||
insa
|
||||
led-manager
|
||||
persistence-manager
|
||||
SDL3::SDL3
|
||||
SDL3_image::SDL3_image
|
||||
SDL3_ttf::SDL3_ttf
|
||||
u8g2
|
||||
)
|
||||
endif ()
|
||||
661
firmware/LICENSE
Normal file
661
firmware/LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
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), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
15
firmware/README.md
Executable file
15
firmware/README.md
Executable file
@@ -0,0 +1,15 @@
|
||||
## Systen Control
|
||||
|
||||
### ESP32-S3 (folder: main)
|
||||
|
||||
This is an implementation of my custom system control project (custom pcb with Lolin ESP32-S3 Mini) and LED strip.
|
||||
|
||||
The build process is straight forward with ESP-IDF. We used version 5.4 while development and the github actions tried to compile for multiple ESP-IDF versions, so we are safe.
|
||||
|
||||
### Desktop (folder: src)
|
||||
|
||||
It's included also a desktop application (with SDL3), so you can test the project without any MCU.
|
||||
|
||||
### Global Information
|
||||
|
||||
The projects can be generated from the root, because here is the starting CMakeLists.txt file.
|
||||
BIN
firmware/assets/Helvetica-Bold.otf
Normal file
BIN
firmware/assets/Helvetica-Bold.otf
Normal file
Binary file not shown.
BIN
firmware/assets/button_normal.png
Normal file
BIN
firmware/assets/button_normal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 326 B |
BIN
firmware/assets/button_pressed_overlay.png
Normal file
BIN
firmware/assets/button_pressed_overlay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 511 B |
BIN
firmware/assets/d-pad_normal.png
Normal file
BIN
firmware/assets/d-pad_normal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 815 B |
BIN
firmware/assets/haxrcorp-4089.otf
Normal file
BIN
firmware/assets/haxrcorp-4089.otf
Normal file
Binary file not shown.
12
firmware/bootloader_components/main/CMakeLists.txt
Normal file
12
firmware/bootloader_components/main/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
idf_component_register(SRCS "bootloader.c"
|
||||
REQUIRES bootloader bootloader_support)
|
||||
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(target_folder "${target}")
|
||||
|
||||
# Use the linker script files from the actual bootloader
|
||||
set(scripts "${IDF_PATH}/components/bootloader/subproject/main/ld/${target_folder}/bootloader.ld"
|
||||
"${IDF_PATH}/components/bootloader/subproject/main/ld/${target_folder}/bootloader.rom.ld")
|
||||
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}")
|
||||
65
firmware/bootloader_components/main/bootloader.c
Normal file
65
firmware/bootloader_components/main/bootloader.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "bootloader_init.h"
|
||||
#include "bootloader_utility.h"
|
||||
#include "bootloader_common.h"
|
||||
|
||||
static const char* TAG = "boot";
|
||||
|
||||
static int select_partition_number(bootloader_state_t* bs);
|
||||
|
||||
/*
|
||||
* We arrive here after the ROM bootloader finished loading this second stage bootloader from flash.
|
||||
* The hardware is mostly uninitialized, flash cache is down and the app CPU is in reset.
|
||||
* We do have a stack, so we can do the initialization in C.
|
||||
*/
|
||||
void __attribute__((noreturn)) call_start_cpu0(void) {
|
||||
// 1. Hardware initialization
|
||||
if(bootloader_init() != ESP_OK) {
|
||||
bootloader_reset();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
|
||||
// If this boot is a wake up from the deep sleep then go to the short way,
|
||||
// try to load the application which worked before deep sleep.
|
||||
// It skips a lot of checks due to it was done before (while first boot).
|
||||
bootloader_utility_load_boot_image_from_deep_sleep();
|
||||
// If it is not successful try to load an application as usual.
|
||||
#endif
|
||||
|
||||
// 2. Select the number of boot partition
|
||||
bootloader_state_t bs = {0};
|
||||
int boot_index = select_partition_number(&bs);
|
||||
if(boot_index == INVALID_INDEX) {
|
||||
bootloader_reset();
|
||||
}
|
||||
|
||||
// 2.1 Print a custom message!
|
||||
ESP_LOGI(TAG, "Custom bootloader completed");
|
||||
|
||||
// 3. Load the app image for booting
|
||||
bootloader_utility_load_boot_image(&bs, boot_index);
|
||||
}
|
||||
|
||||
// Select the number of boot partition
|
||||
static int select_partition_number(bootloader_state_t* bs) {
|
||||
// 1. Load partition table
|
||||
if(!bootloader_utility_load_partition_table(bs)) {
|
||||
ESP_LOGE(TAG, "load partition table error!");
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
// 2. Select the number of boot partition
|
||||
return bootloader_utility_get_selected_boot_partition(bs);
|
||||
}
|
||||
|
||||
// Return global reent struct if any newlib functions are linked to bootloader
|
||||
struct _reent* __getreent(void) {
|
||||
return _GLOBAL_REENT;
|
||||
}
|
||||
10
firmware/components/CMakeLists.txt
Normal file
10
firmware/components/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
if (NOT DEFINED ENV{IDF_PATH})
|
||||
add_library(components INTERFACE)
|
||||
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(insa)
|
||||
add_subdirectory(led-manager)
|
||||
add_subdirectory(persistence-manager)
|
||||
|
||||
target_link_libraries(components INTERFACE ImGui)
|
||||
endif ()
|
||||
23
firmware/components/imgui/CMakeLists.txt
Normal file
23
firmware/components/imgui/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(ImGui)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
imgui.cpp
|
||||
imgui_demo.cpp
|
||||
imgui_draw.cpp
|
||||
imgui_widgets.cpp
|
||||
imgui_tables.cpp
|
||||
|
||||
imgui_impl_sdl3.cpp
|
||||
imgui_impl_sdlrenderer3.cpp
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
|
||||
17807
firmware/components/imgui/imgui.cpp
Normal file
17807
firmware/components/imgui/imgui.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10782
firmware/components/imgui/imgui_demo.cpp
Normal file
10782
firmware/components/imgui/imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
6242
firmware/components/imgui/imgui_draw.cpp
Normal file
6242
firmware/components/imgui/imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
826
firmware/components/imgui/imgui_impl_sdl3.cpp
Normal file
826
firmware/components/imgui/imgui_impl_sdl3.cpp
Normal file
@@ -0,0 +1,826 @@
|
||||
// dear imgui: Platform Backend for SDL3
|
||||
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: IME support.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-04-22: IME: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible.
|
||||
// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561)
|
||||
// 2025-03-30: Update for SDL3 api changes: Revert SDL_GetClipboardText() memory ownership change. (#8530, #7801)
|
||||
// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
|
||||
// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468)
|
||||
// 2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g.Linux debuggers not claiming capture back. (#6410, #3650)
|
||||
// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode.
|
||||
// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
|
||||
// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler.
|
||||
// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array.
|
||||
// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten.
|
||||
// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
|
||||
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
|
||||
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
|
||||
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
|
||||
// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
|
||||
// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
|
||||
// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
|
||||
// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807)
|
||||
// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801)
|
||||
// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794)
|
||||
// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762).
|
||||
// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea().
|
||||
// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures.
|
||||
// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents.
|
||||
// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames.
|
||||
// 2024-05-15: Update for SDL3 api changes: SDLK_ renames.
|
||||
// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME.
|
||||
// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode().
|
||||
// 2023-11-13: Updated for recent SDL3 API changes.
|
||||
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||
// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391)
|
||||
// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
|
||||
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
|
||||
// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
|
||||
// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#endif
|
||||
|
||||
// SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdio.h> // for snprintf()
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
|
||||
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
|
||||
#else
|
||||
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
|
||||
#endif
|
||||
|
||||
// FIXME-LEGACY: remove when SDL 3.1.3 preview is released.
|
||||
#ifndef SDLK_APOSTROPHE
|
||||
#define SDLK_APOSTROPHE SDLK_QUOTE
|
||||
#endif
|
||||
#ifndef SDLK_GRAVE
|
||||
#define SDLK_GRAVE SDLK_BACKQUOTE
|
||||
#endif
|
||||
|
||||
// SDL Data
|
||||
struct ImGui_ImplSDL3_Data
|
||||
{
|
||||
SDL_Window* Window;
|
||||
SDL_WindowID WindowID;
|
||||
SDL_Renderer* Renderer;
|
||||
Uint64 Time;
|
||||
char* ClipboardTextData;
|
||||
char BackendPlatformName[48];
|
||||
|
||||
// IME handling
|
||||
SDL_Window* ImeWindow;
|
||||
|
||||
// Mouse handling
|
||||
Uint32 MouseWindowID;
|
||||
int MouseButtonsDown;
|
||||
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||
SDL_Cursor* MouseLastCursor;
|
||||
int MousePendingLeaveFrame;
|
||||
bool MouseCanUseGlobalState;
|
||||
bool MouseCanUseCapture;
|
||||
|
||||
// Gamepad handling
|
||||
ImVector<SDL_Gamepad*> Gamepads;
|
||||
ImGui_ImplSDL3_GamepadMode GamepadMode;
|
||||
bool WantUpdateGamepadsList;
|
||||
|
||||
ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||
static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||
}
|
||||
|
||||
// Functions
|
||||
static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
if (bd->ClipboardTextData)
|
||||
SDL_free(bd->ClipboardTextData);
|
||||
bd->ClipboardTextData = SDL_GetClipboardText();
|
||||
return bd->ClipboardTextData;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text)
|
||||
{
|
||||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
|
||||
SDL_Window* window = SDL_GetWindowFromID(window_id);
|
||||
if ((!(data->WantVisible || data->WantTextInput) || bd->ImeWindow != window) && bd->ImeWindow != nullptr)
|
||||
{
|
||||
SDL_StopTextInput(bd->ImeWindow);
|
||||
bd->ImeWindow = nullptr;
|
||||
}
|
||||
if (data->WantVisible)
|
||||
{
|
||||
SDL_Rect r;
|
||||
r.x = (int)data->InputPos.x;
|
||||
r.y = (int)data->InputPos.y;
|
||||
r.w = 1;
|
||||
r.h = (int)data->InputLineHeight;
|
||||
SDL_SetTextInputArea(window, &r, 0);
|
||||
bd->ImeWindow = window;
|
||||
}
|
||||
if (data->WantVisible || data->WantTextInput)
|
||||
SDL_StartTextInput(window);
|
||||
}
|
||||
|
||||
// Not static to allow third-party code to use that if they want to (but undocumented)
|
||||
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
|
||||
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
|
||||
{
|
||||
// Keypad doesn't have individual key values in SDL3
|
||||
switch (scancode)
|
||||
{
|
||||
case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0;
|
||||
case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1;
|
||||
case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2;
|
||||
case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3;
|
||||
case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4;
|
||||
case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5;
|
||||
case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6;
|
||||
case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7;
|
||||
case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8;
|
||||
case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9;
|
||||
case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal;
|
||||
case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||
case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||
case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract;
|
||||
case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd;
|
||||
case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter;
|
||||
case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual;
|
||||
default: break;
|
||||
}
|
||||
switch (keycode)
|
||||
{
|
||||
case SDLK_TAB: return ImGuiKey_Tab;
|
||||
case SDLK_LEFT: return ImGuiKey_LeftArrow;
|
||||
case SDLK_RIGHT: return ImGuiKey_RightArrow;
|
||||
case SDLK_UP: return ImGuiKey_UpArrow;
|
||||
case SDLK_DOWN: return ImGuiKey_DownArrow;
|
||||
case SDLK_PAGEUP: return ImGuiKey_PageUp;
|
||||
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
|
||||
case SDLK_HOME: return ImGuiKey_Home;
|
||||
case SDLK_END: return ImGuiKey_End;
|
||||
case SDLK_INSERT: return ImGuiKey_Insert;
|
||||
case SDLK_DELETE: return ImGuiKey_Delete;
|
||||
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
|
||||
case SDLK_SPACE: return ImGuiKey_Space;
|
||||
case SDLK_RETURN: return ImGuiKey_Enter;
|
||||
case SDLK_ESCAPE: return ImGuiKey_Escape;
|
||||
//case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe;
|
||||
case SDLK_COMMA: return ImGuiKey_Comma;
|
||||
//case SDLK_MINUS: return ImGuiKey_Minus;
|
||||
case SDLK_PERIOD: return ImGuiKey_Period;
|
||||
//case SDLK_SLASH: return ImGuiKey_Slash;
|
||||
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
|
||||
//case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||
//case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||
//case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||
//case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||
//case SDLK_GRAVE: return ImGuiKey_GraveAccent;
|
||||
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
|
||||
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
|
||||
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
|
||||
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
|
||||
case SDLK_PAUSE: return ImGuiKey_Pause;
|
||||
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
|
||||
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
|
||||
case SDLK_LALT: return ImGuiKey_LeftAlt;
|
||||
case SDLK_LGUI: return ImGuiKey_LeftSuper;
|
||||
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
|
||||
case SDLK_RSHIFT: return ImGuiKey_RightShift;
|
||||
case SDLK_RALT: return ImGuiKey_RightAlt;
|
||||
case SDLK_RGUI: return ImGuiKey_RightSuper;
|
||||
case SDLK_APPLICATION: return ImGuiKey_Menu;
|
||||
case SDLK_0: return ImGuiKey_0;
|
||||
case SDLK_1: return ImGuiKey_1;
|
||||
case SDLK_2: return ImGuiKey_2;
|
||||
case SDLK_3: return ImGuiKey_3;
|
||||
case SDLK_4: return ImGuiKey_4;
|
||||
case SDLK_5: return ImGuiKey_5;
|
||||
case SDLK_6: return ImGuiKey_6;
|
||||
case SDLK_7: return ImGuiKey_7;
|
||||
case SDLK_8: return ImGuiKey_8;
|
||||
case SDLK_9: return ImGuiKey_9;
|
||||
case SDLK_A: return ImGuiKey_A;
|
||||
case SDLK_B: return ImGuiKey_B;
|
||||
case SDLK_C: return ImGuiKey_C;
|
||||
case SDLK_D: return ImGuiKey_D;
|
||||
case SDLK_E: return ImGuiKey_E;
|
||||
case SDLK_F: return ImGuiKey_F;
|
||||
case SDLK_G: return ImGuiKey_G;
|
||||
case SDLK_H: return ImGuiKey_H;
|
||||
case SDLK_I: return ImGuiKey_I;
|
||||
case SDLK_J: return ImGuiKey_J;
|
||||
case SDLK_K: return ImGuiKey_K;
|
||||
case SDLK_L: return ImGuiKey_L;
|
||||
case SDLK_M: return ImGuiKey_M;
|
||||
case SDLK_N: return ImGuiKey_N;
|
||||
case SDLK_O: return ImGuiKey_O;
|
||||
case SDLK_P: return ImGuiKey_P;
|
||||
case SDLK_Q: return ImGuiKey_Q;
|
||||
case SDLK_R: return ImGuiKey_R;
|
||||
case SDLK_S: return ImGuiKey_S;
|
||||
case SDLK_T: return ImGuiKey_T;
|
||||
case SDLK_U: return ImGuiKey_U;
|
||||
case SDLK_V: return ImGuiKey_V;
|
||||
case SDLK_W: return ImGuiKey_W;
|
||||
case SDLK_X: return ImGuiKey_X;
|
||||
case SDLK_Y: return ImGuiKey_Y;
|
||||
case SDLK_Z: return ImGuiKey_Z;
|
||||
case SDLK_F1: return ImGuiKey_F1;
|
||||
case SDLK_F2: return ImGuiKey_F2;
|
||||
case SDLK_F3: return ImGuiKey_F3;
|
||||
case SDLK_F4: return ImGuiKey_F4;
|
||||
case SDLK_F5: return ImGuiKey_F5;
|
||||
case SDLK_F6: return ImGuiKey_F6;
|
||||
case SDLK_F7: return ImGuiKey_F7;
|
||||
case SDLK_F8: return ImGuiKey_F8;
|
||||
case SDLK_F9: return ImGuiKey_F9;
|
||||
case SDLK_F10: return ImGuiKey_F10;
|
||||
case SDLK_F11: return ImGuiKey_F11;
|
||||
case SDLK_F12: return ImGuiKey_F12;
|
||||
case SDLK_F13: return ImGuiKey_F13;
|
||||
case SDLK_F14: return ImGuiKey_F14;
|
||||
case SDLK_F15: return ImGuiKey_F15;
|
||||
case SDLK_F16: return ImGuiKey_F16;
|
||||
case SDLK_F17: return ImGuiKey_F17;
|
||||
case SDLK_F18: return ImGuiKey_F18;
|
||||
case SDLK_F19: return ImGuiKey_F19;
|
||||
case SDLK_F20: return ImGuiKey_F20;
|
||||
case SDLK_F21: return ImGuiKey_F21;
|
||||
case SDLK_F22: return ImGuiKey_F22;
|
||||
case SDLK_F23: return ImGuiKey_F23;
|
||||
case SDLK_F24: return ImGuiKey_F24;
|
||||
case SDLK_AC_BACK: return ImGuiKey_AppBack;
|
||||
case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Fallback to scancode
|
||||
switch (scancode)
|
||||
{
|
||||
case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
|
||||
case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
|
||||
case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
|
||||
case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||
case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||
case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102;
|
||||
case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
|
||||
case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
|
||||
case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
|
||||
case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
|
||||
case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
|
||||
case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
|
||||
default: break;
|
||||
}
|
||||
return ImGuiKey_None;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
|
||||
}
|
||||
|
||||
|
||||
static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr;
|
||||
}
|
||||
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
|
||||
bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr)
|
||||
return false;
|
||||
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
|
||||
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr)
|
||||
return false;
|
||||
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
|
||||
float wheel_x = -event->wheel.x;
|
||||
float wheel_y = event->wheel.y;
|
||||
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMouseWheelEvent(wheel_x, wheel_y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr)
|
||||
return false;
|
||||
int mouse_button = -1;
|
||||
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
|
||||
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
|
||||
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
|
||||
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
|
||||
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
|
||||
if (mouse_button == -1)
|
||||
break;
|
||||
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
|
||||
bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr)
|
||||
return false;
|
||||
io.AddInputCharactersUTF8(event->text.text);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr)
|
||||
return false;
|
||||
ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
|
||||
//IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n",
|
||||
// (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod);
|
||||
ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
|
||||
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
||||
io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
bd->MouseWindowID = event->window.windowID;
|
||||
bd->MousePendingLeaveFrame = 0;
|
||||
return true;
|
||||
}
|
||||
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
|
||||
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
|
||||
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
|
||||
// FIXME: Unconfirmed whether this is still needed with SDL3.
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
{
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
|
||||
{
|
||||
viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
|
||||
viewport->PlatformHandleRaw = nullptr;
|
||||
#if defined(_WIN32) && !defined(__WINRT__)
|
||||
viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||
viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||
IM_UNUSED(sdl_gl_context); // Unused in this branch
|
||||
|
||||
const int ver_linked = SDL_GetVersion();
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
|
||||
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%u.%u.%u; %u.%u.%u)",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = bd->BackendPlatformName;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
|
||||
bd->Window = window;
|
||||
bd->WindowID = SDL_GetWindowID(window);
|
||||
bd->Renderer = renderer;
|
||||
|
||||
// Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
|
||||
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
|
||||
bd->MouseCanUseGlobalState = false;
|
||||
bd->MouseCanUseCapture = false;
|
||||
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||
const char* sdl_backend = SDL_GetCurrentVideoDriver();
|
||||
const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
|
||||
for (const char* item : capture_and_global_state_whitelist)
|
||||
if (strncmp(sdl_backend, item, strlen(item)) == 0)
|
||||
bd->MouseCanUseGlobalState = bd->MouseCanUseCapture = true;
|
||||
#endif
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
|
||||
platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
|
||||
platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
|
||||
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; };
|
||||
|
||||
// Gamepad handling
|
||||
bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
|
||||
// Load mouse cursors
|
||||
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS);
|
||||
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED);
|
||||
|
||||
// Set platform dependent data in viewport
|
||||
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||
ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window);
|
||||
|
||||
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
|
||||
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
|
||||
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
|
||||
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
|
||||
// you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||
#endif
|
||||
|
||||
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
|
||||
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
|
||||
{
|
||||
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, renderer, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForOther(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_CloseGamepads();
|
||||
|
||||
void ImGui_ImplSDL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
if (bd->ClipboardTextData)
|
||||
SDL_free(bd->ClipboardTextData);
|
||||
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||
SDL_DestroyCursor(bd->MouseCursors[cursor_n]);
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
|
||||
io.BackendPlatformName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateMouseData()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
|
||||
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||
// - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside.
|
||||
// - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue we wait until mouse has moved to begin capture.
|
||||
if (bd->MouseCanUseCapture)
|
||||
{
|
||||
bool want_capture = false;
|
||||
for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++)
|
||||
if (ImGui::IsMouseDragging(button_n, 1.0f))
|
||||
want_capture = true;
|
||||
SDL_CaptureMouse(want_capture);
|
||||
}
|
||||
|
||||
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||
const bool is_app_focused = (bd->Window == focused_window);
|
||||
#else
|
||||
SDL_Window* focused_window = bd->Window;
|
||||
const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
|
||||
#endif
|
||||
if (is_app_focused)
|
||||
{
|
||||
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
|
||||
|
||||
// (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured)
|
||||
const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window);
|
||||
if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode)
|
||||
{
|
||||
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
||||
float mouse_x_global, mouse_y_global;
|
||||
int window_x, window_y;
|
||||
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
|
||||
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
|
||||
io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return;
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
SDL_HideCursor();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
|
||||
if (bd->MouseLastCursor != expected_cursor)
|
||||
{
|
||||
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
|
||||
bd->MouseLastCursor = expected_cursor;
|
||||
}
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_CloseGamepads()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
bd->Gamepads.resize(0);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
if (mode == ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
{
|
||||
IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0);
|
||||
for (int n = 0; n < manual_gamepads_count; n++)
|
||||
bd->Gamepads.push_back(manual_gamepads_array[n]);
|
||||
}
|
||||
else
|
||||
{
|
||||
IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
}
|
||||
bd->GamepadMode = mode;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no)
|
||||
{
|
||||
bool merged_value = false;
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
|
||||
io.AddKeyEvent(key, merged_value);
|
||||
}
|
||||
|
||||
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
|
||||
static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1)
|
||||
{
|
||||
float merged_value = 0.0f;
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
{
|
||||
float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
|
||||
if (merged_value < vn)
|
||||
merged_value = vn;
|
||||
}
|
||||
io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateGamepads()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
|
||||
// Update list of gamepads to use
|
||||
if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
{
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
int sdl_gamepads_count = 0;
|
||||
SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
|
||||
for (int n = 0; n < sdl_gamepads_count; n++)
|
||||
if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n]))
|
||||
{
|
||||
bd->Gamepads.push_back(gamepad);
|
||||
if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
|
||||
break;
|
||||
}
|
||||
bd->WantUpdateGamepadsList = false;
|
||||
SDL_free(sdl_gamepads);
|
||||
}
|
||||
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
if (bd->Gamepads.Size == 0)
|
||||
return;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
// Update gamepad inputs
|
||||
const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value.
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(SDL_Window* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)
|
||||
{
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
|
||||
w = h = 0;
|
||||
SDL_GetWindowSizeInPixels(window, &display_w, &display_h);
|
||||
if (out_size != nullptr)
|
||||
*out_size = ImVec2((float)w, (float)h);
|
||||
if (out_framebuffer_scale != nullptr)
|
||||
*out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / w, (float)display_h / h) : ImVec2(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL3_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Setup main viewport size (every frame to accommodate for window resizing)
|
||||
ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);
|
||||
|
||||
// Setup time step (we could also use SDL_GetTicksNS() available since SDL3)
|
||||
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
|
||||
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||
if (current_time <= bd->Time)
|
||||
current_time = bd->Time + 1;
|
||||
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
|
||||
bd->Time = current_time;
|
||||
|
||||
if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||
{
|
||||
bd->MouseWindowID = 0;
|
||||
bd->MousePendingLeaveFrame = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
}
|
||||
|
||||
ImGui_ImplSDL3_UpdateMouseData();
|
||||
ImGui_ImplSDL3_UpdateMouseCursor();
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplSDL3_UpdateGamepads();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
314
firmware/components/imgui/imgui_impl_sdlrenderer3.cpp
Normal file
314
firmware/components/imgui/imgui_impl_sdlrenderer3.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||
// (Requires: SDL 3.1.8+)
|
||||
|
||||
// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete.
|
||||
// For a multi-platform app consider using other technologies:
|
||||
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API.
|
||||
// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers.
|
||||
// If your application wants to render any non trivial amount of graphics other than UI,
|
||||
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
|
||||
// and it might be difficult to step out of those boundaries.
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture().
|
||||
// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color.
|
||||
// 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
|
||||
// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
|
||||
// 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly.
|
||||
// 2023-05-30: Initial version.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_sdlrenderer3.h"
|
||||
#include <stdint.h> // intptr_t
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#if !SDL_VERSION_ATLEAST(3,0,0)
|
||||
#error This backend requires SDL 3.0.0+
|
||||
#endif
|
||||
|
||||
// SDL_Renderer data
|
||||
struct ImGui_ImplSDLRenderer3_Data
|
||||
{
|
||||
SDL_Renderer* Renderer; // Main viewport's renderer
|
||||
ImVector<SDL_FColor> ColorBuffer;
|
||||
|
||||
ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||
}
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_sdlrenderer3";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
bd->Renderer = renderer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_Shutdown()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer)
|
||||
{
|
||||
// Clear out any viewports and cliprect set by the user
|
||||
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
|
||||
SDL_SetRenderViewport(renderer, nullptr);
|
||||
SDL_SetRenderClipRect(renderer, nullptr);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?");
|
||||
IM_UNUSED(bd);
|
||||
}
|
||||
|
||||
// https://github.com/libsdl-org/SDL/issues/9009
|
||||
static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector<SDL_FColor>& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices)
|
||||
{
|
||||
const Uint8* color2 = (const Uint8*)color;
|
||||
colors_out.resize(num_vertices);
|
||||
SDL_FColor* color3 = colors_out.Data;
|
||||
for (int i = 0; i < num_vertices; i++)
|
||||
{
|
||||
color3[i].r = color->r / 255.0f;
|
||||
color3[i].g = color->g / 255.0f;
|
||||
color3[i].b = color->b / 255.0f;
|
||||
color3[i].a = color->a / 255.0f;
|
||||
color2 += color_stride;
|
||||
color = (const SDL_Color*)color2;
|
||||
}
|
||||
return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
|
||||
// If there's a scale factor set by the user, use that instead
|
||||
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
|
||||
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
|
||||
float rsx = 1.0f;
|
||||
float rsy = 1.0f;
|
||||
SDL_GetRenderScale(renderer, &rsx, &rsy);
|
||||
ImVec2 render_scale;
|
||||
render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
|
||||
render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
|
||||
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
|
||||
if (fb_width == 0 || fb_height == 0)
|
||||
return;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
|
||||
|
||||
// Backup SDL_Renderer state that will be modified to restore it afterwards
|
||||
struct BackupSDLRendererState
|
||||
{
|
||||
SDL_Rect Viewport;
|
||||
bool ViewportEnabled;
|
||||
bool ClipEnabled;
|
||||
SDL_Rect ClipRect;
|
||||
};
|
||||
BackupSDLRendererState old = {};
|
||||
old.ViewportEnabled = SDL_RenderViewportSet(renderer);
|
||||
old.ClipEnabled = SDL_RenderClipEnabled(renderer);
|
||||
SDL_GetRenderViewport(renderer, &old.Viewport);
|
||||
SDL_GetRenderClipRect(renderer, &old.ClipRect);
|
||||
|
||||
// Setup desired state
|
||||
ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
|
||||
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplSDLRenderer3_RenderState render_state;
|
||||
render_state.Renderer = renderer;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = render_scale;
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* draw_list = draw_data->CmdLists[n];
|
||||
const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
|
||||
const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
|
||||
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||
if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
|
||||
if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
|
||||
SDL_SetRenderClipRect(renderer, &r);
|
||||
|
||||
const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));
|
||||
const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));
|
||||
const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+
|
||||
|
||||
// Bind texture, Draw
|
||||
SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
|
||||
SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex,
|
||||
xy, (int)sizeof(ImDrawVert),
|
||||
color, (int)sizeof(ImDrawVert),
|
||||
uv, (int)sizeof(ImDrawVert),
|
||||
draw_list->VtxBuffer.Size - pcmd->VtxOffset,
|
||||
idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Restore modified SDL_Renderer state
|
||||
SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr);
|
||||
SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
|
||||
// Create texture
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height);
|
||||
IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!");
|
||||
SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch());
|
||||
SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR);
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)sdl_texture);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
{
|
||||
SDL_Rect sdl_r = { r.x, r.y, r.w, r.h };
|
||||
SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch());
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
{
|
||||
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
|
||||
if (sdl_texture == nullptr)
|
||||
return;
|
||||
SDL_DestroyTexture(sdl_texture);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_CreateDeviceObjects()
|
||||
{
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
|
||||
{
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
{
|
||||
tex->SetStatus(ImTextureStatus_WantDestroy);
|
||||
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
4559
firmware/components/imgui/imgui_tables.cpp
Normal file
4559
firmware/components/imgui/imgui_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10604
firmware/components/imgui/imgui_widgets.cpp
Normal file
10604
firmware/components/imgui/imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
141
firmware/components/imgui/include/imconfig.h
Normal file
141
firmware/components/imgui/include/imconfig.h
Normal file
@@ -0,0 +1,141 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
4113
firmware/components/imgui/include/imgui.h
Normal file
4113
firmware/components/imgui/include/imgui.h
Normal file
File diff suppressed because it is too large
Load Diff
47
firmware/components/imgui/include/imgui_impl_sdl3.h
Normal file
47
firmware/components/imgui/include/imgui_impl_sdl3.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// dear imgui: Platform Backend for SDL3
|
||||
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: IME support.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Gamepad;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
|
||||
|
||||
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
|
||||
// When using manual mode, caller is responsible for opening/closing gamepad.
|
||||
enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual };
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
53
firmware/components/imgui/include/imgui_impl_sdlrenderer3.h
Normal file
53
firmware/components/imgui/include/imgui_impl_sdlrenderer3.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||
// (Requires: SDL 3.1.8+)
|
||||
|
||||
// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete.
|
||||
// For a multi-platform app consider using other technologies:
|
||||
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API.
|
||||
// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers.
|
||||
// If your application wants to render any non trivial amount of graphics other than UI,
|
||||
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
|
||||
// and it might be difficult to step out of those boundaries.
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Renderer;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer3_RenderDrawData() call.
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplSDLRenderer3_RenderState
|
||||
{
|
||||
SDL_Renderer* Renderer;
|
||||
};
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
3892
firmware/components/imgui/include/imgui_internal.h
Normal file
3892
firmware/components/imgui/include/imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
627
firmware/components/imgui/include/imstb_rectpack.h
Normal file
627
firmware/components/imgui/include/imstb_rectpack.h
Normal file
@@ -0,0 +1,627 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
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.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
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 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.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1488
firmware/components/imgui/include/imstb_textedit.h
Normal file
1488
firmware/components/imgui/include/imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
5085
firmware/components/imgui/include/imstb_truetype.h
Normal file
5085
firmware/components/imgui/include/imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
43
firmware/components/insa/CMakeLists.txt
Normal file
43
firmware/components/insa/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
# Definiere die Quelldateien in einer Variable
|
||||
set(SOURCE_FILES
|
||||
src/common/InactivityTracker.cpp
|
||||
src/common/Menu.cpp
|
||||
src/common/ScrollBar.cpp
|
||||
src/common/Widget.cpp
|
||||
src/data/MenuItem.cpp
|
||||
src/ui/LightMenu.cpp
|
||||
src/ui/LightSettingsMenu.cpp
|
||||
src/ui/MainMenu.cpp
|
||||
src/ui/ScreenSaver.cpp
|
||||
src/ui/SettingsMenu.cpp
|
||||
src/ui/SplashScreen.cpp
|
||||
)
|
||||
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
idf_component_register(SRCS
|
||||
${SOURCE_FILES}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
u8g2
|
||||
led-manager
|
||||
persistence-manager
|
||||
)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(insa)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
${SOURCE_FILES}
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
u8g2
|
||||
led-manager
|
||||
persistence-manager
|
||||
)
|
||||
74
firmware/components/insa/include/MenuOptions.h
Normal file
74
firmware/components/insa/include/MenuOptions.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file MenuOptions.h
|
||||
* @brief Menu configuration structure and callback definitions
|
||||
* @details This header defines the menu_options_t structure which contains all
|
||||
* necessary configuration options and callback functions for menu widgets.
|
||||
* It provides the interface between menu widgets and the application's
|
||||
* screen management system, display context, and input handling.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Standard libraries for function objects and smart pointers
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// Project-specific headers
|
||||
#include "common/Widget.h"
|
||||
#include "IPersistenceManager.h"
|
||||
#include "u8g2.h"
|
||||
|
||||
class MenuItem;
|
||||
|
||||
/**
|
||||
* @struct menu_options_t
|
||||
* @brief Configuration structure for menu widgets containing display context and callbacks
|
||||
* @details This structure serves as a configuration container that provides menu widgets
|
||||
* with access to the display system, screen management functions, input
|
||||
* handling callbacks, and persistent storage.
|
||||
*
|
||||
* @see Widget
|
||||
* @see ButtonType
|
||||
* @see IPersistenceManager
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* @brief Pointer to u8g2 display context for graphics output operations
|
||||
*/
|
||||
u8g2_t *u8g2;
|
||||
|
||||
/**
|
||||
* @brief Callback function to set the current active screen
|
||||
*/
|
||||
std::function<void(std::shared_ptr<Widget>)> setScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to add a new screen to the navigation stack
|
||||
*/
|
||||
std::function<void(std::shared_ptr<Widget>)> pushScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to remove the top screen from the navigation stack
|
||||
*/
|
||||
std::function<void()> popScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to handle button press events
|
||||
*/
|
||||
std::function<void(ButtonType button)> onButtonClicked;
|
||||
|
||||
/**
|
||||
* @brief Shared pointer to platform-independent persistence manager
|
||||
* @details This provides access to persistent key-value storage across different
|
||||
* platforms. The actual implementation (SDL3 or ESP32/NVS) is determined
|
||||
* at compile time based on the target platform.
|
||||
*
|
||||
* @note The persistence manager is shared across all menu widgets and maintains
|
||||
* its state throughout the application lifecycle.
|
||||
*/
|
||||
std::shared_ptr<IPersistenceManager> persistenceManager;
|
||||
|
||||
} menu_options_t;
|
||||
63
firmware/components/insa/include/common/Common.h
Normal file
63
firmware/components/insa/include/common/Common.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file Common.h
|
||||
* @brief Common definitions and types for the INSA component
|
||||
* @details This header file contains shared enumerations, type definitions, and
|
||||
* callback function types used throughout the INSA component system.
|
||||
* It provides the foundation for button handling and event management.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* @enum ButtonType
|
||||
* @brief Enumeration defining the different types of buttons available in the system
|
||||
* @details This enumeration represents all possible button types that can be handled
|
||||
* by the system's input management. NONE represents no button pressed or
|
||||
* an invalid button state, while the other values correspond to specific
|
||||
* directional and action buttons.
|
||||
*/
|
||||
enum class ButtonType
|
||||
{
|
||||
NONE, ///< No button pressed or invalid button state
|
||||
UP, ///< Up directional button for navigation
|
||||
DOWN, ///< Down directional button for navigation
|
||||
LEFT, ///< Left directional button for navigation
|
||||
RIGHT, ///< Right directional button for navigation
|
||||
SELECT, ///< Select/confirm button for accepting choices
|
||||
BACK ///< Back/cancel button for returning to previous state
|
||||
};
|
||||
|
||||
// Forward declaration of MenuItem to avoid circular dependency
|
||||
class MenuItem;
|
||||
|
||||
/**
|
||||
* @typedef ButtonCallback
|
||||
* @brief Type alias for button event callback function
|
||||
* @details This function type is used to define callback functions that handle
|
||||
* button press events. The callback receives information about which
|
||||
* button was pressed and any additional context data.
|
||||
*
|
||||
* @param MenuItem menu item for the specific action
|
||||
* @param ButtonType The type of button that was pressed
|
||||
*
|
||||
* @note The first parameter can be used to distinguish between multiple instances
|
||||
* of the same button type or to pass additional event-specific data.
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* ButtonCallback myCallback = [](const MenuItem& item, ButtonType type) {
|
||||
* if (type == ButtonType::SELECT) {
|
||||
* // Handle select button press
|
||||
* processSelection(item);
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
typedef std::function<void(MenuItem, ButtonType)> ButtonCallback;
|
||||
|
||||
// Include MenuItem.h after the typedef to avoid circular dependency
|
||||
#include "data/MenuItem.h"
|
||||
194
firmware/components/insa/include/common/InactivityTracker.h
Normal file
194
firmware/components/insa/include/common/InactivityTracker.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @file InactivityTracker.h
|
||||
* @brief Inactivity tracking system for monitoring user interaction timeouts
|
||||
* @details This header defines the InactivityTracker class which monitors user
|
||||
* activity and triggers timeout callbacks when the system remains inactive
|
||||
* for a specified duration. It provides essential functionality for power
|
||||
* management, screen savers, and automatic system state transitions.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @class InactivityTracker
|
||||
* @brief Activity monitoring class for detecting user inactivity periods
|
||||
* @details This class provides a robust mechanism for tracking user activity and
|
||||
* triggering automatic actions when the system remains inactive for a
|
||||
* configured timeout period. It is commonly used for implementing power
|
||||
* saving features, automatic screen savers, session timeouts, and other
|
||||
* time-based system behaviors.
|
||||
*
|
||||
* The InactivityTracker operates by:
|
||||
* - Continuously tracking elapsed time since the last user activity
|
||||
* - Comparing elapsed time against a configurable timeout threshold
|
||||
* - Executing a callback function when the timeout is reached
|
||||
* - Providing methods to reset the timer when activity is detected
|
||||
* - Supporting enable/disable functionality for dynamic control
|
||||
*
|
||||
* Key features include:
|
||||
* - Configurable timeout duration in milliseconds
|
||||
* - Custom callback function execution on timeout
|
||||
* - Activity reset capability for responsive user interaction
|
||||
* - Enable/disable control for conditional monitoring
|
||||
* - High-resolution timing support using 64-bit millisecond precision
|
||||
*
|
||||
* Common use cases:
|
||||
* - Screen saver activation after idle periods
|
||||
* - Automatic screen dimming or shutdown
|
||||
* - Session timeout management
|
||||
* - Power management and battery conservation
|
||||
* - User interface state transitions
|
||||
* - Security lockout after inactivity
|
||||
*
|
||||
* The class is designed to be lightweight and efficient, suitable for
|
||||
* real-time applications where precise timing and minimal overhead are important.
|
||||
*
|
||||
* @note This class requires regular update calls to function properly.
|
||||
* @note The timeout callback is executed once per timeout period and will
|
||||
* not repeat until the tracker is reset and times out again.
|
||||
*
|
||||
* @see Widget for integration with UI components
|
||||
* @see Menu for menu timeout implementations
|
||||
*/
|
||||
class InactivityTracker
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an InactivityTracker with specified timeout and callback
|
||||
* @param timeoutMs Timeout duration in milliseconds before triggering callback
|
||||
* @param onTimeout Callback function to execute when timeout is reached
|
||||
*
|
||||
* @pre timeoutMs must be greater than 0 for meaningful timeout behavior
|
||||
* @pre onTimeout must be a valid callable function object
|
||||
* @post InactivityTracker is initialized, enabled, and ready for activity monitoring
|
||||
*
|
||||
* @details The constructor initializes the inactivity tracker with the specified
|
||||
* timeout duration and callback function. The tracker starts in an enabled
|
||||
* state with zero elapsed time, ready to begin monitoring user activity.
|
||||
*
|
||||
* The timeout callback function can perform any necessary actions when inactivity
|
||||
* is detected, such as:
|
||||
* - Activating screen savers or power saving modes
|
||||
* - Transitioning to different application states
|
||||
* - Logging inactivity events
|
||||
* - Triggering security lockouts
|
||||
* - Initiating automatic save operations
|
||||
*
|
||||
* @note The tracker begins monitoring immediately upon construction.
|
||||
* @note The callback function should be lightweight to avoid blocking
|
||||
* the main application thread during timeout processing.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* auto tracker = InactivityTracker(30000, []() {
|
||||
* // Activate screen saver after 30 seconds of inactivity
|
||||
* activateScreenSaver();
|
||||
* });
|
||||
* @endcode
|
||||
*/
|
||||
InactivityTracker(uint64_t timeoutMs, const std::function<void()> &onTimeout);
|
||||
|
||||
/**
|
||||
* @brief Updates the inactivity timer and checks for timeout conditions
|
||||
* @param dt Delta time in milliseconds since the last update call
|
||||
*
|
||||
* @details This method must be called regularly (typically every frame) to
|
||||
* maintain accurate timing and timeout detection. It increments the
|
||||
* elapsed time counter and triggers the timeout callback when the
|
||||
* configured timeout duration is reached.
|
||||
*
|
||||
* The update process:
|
||||
* - Adds the delta time to the elapsed time counter (if enabled)
|
||||
* - Compares elapsed time against the configured timeout threshold
|
||||
* - Executes the timeout callback if the threshold is exceeded
|
||||
* - Continues monitoring until reset or disabled
|
||||
*
|
||||
* @note This method should be called consistently from the main application
|
||||
* loop to ensure accurate timing behavior.
|
||||
* @note The timeout callback is executed only once per timeout period.
|
||||
* @note If the tracker is disabled, elapsed time is not updated.
|
||||
*
|
||||
* @see reset() to restart the inactivity timer
|
||||
* @see setEnabled() to control monitoring state
|
||||
*/
|
||||
void update(uint64_t dt);
|
||||
|
||||
/**
|
||||
* @brief Resets the inactivity timer to indicate recent user activity
|
||||
*
|
||||
* @details This method should be called whenever user activity is detected
|
||||
* to restart the inactivity timeout period. It resets the elapsed
|
||||
* time counter to zero, effectively extending the timeout deadline
|
||||
* and preventing timeout callback execution until the full timeout
|
||||
* duration elapses again without further resets.
|
||||
*
|
||||
* Common scenarios for calling reset():
|
||||
* - Button presses or key events
|
||||
* - Mouse movement or touch input
|
||||
* - Menu navigation or selection actions
|
||||
* - Any user interface interaction
|
||||
* - System activity that should extend the timeout
|
||||
*
|
||||
* @post Elapsed time is reset to zero, restarting the timeout period
|
||||
*
|
||||
* @note This method can be called at any time, even when the tracker
|
||||
* is disabled, to prepare for future monitoring.
|
||||
* @note Frequent reset calls from active user interaction will prevent
|
||||
* timeout callback execution, which is the intended behavior.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* void onButtonPress() {
|
||||
* tracker.reset(); // User activity detected, restart timeout
|
||||
* // Handle button press...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief Enables or disables inactivity monitoring
|
||||
* @param enabled True to enable monitoring, false to disable
|
||||
*
|
||||
* @details This method controls whether the inactivity tracker actively
|
||||
* monitors for timeouts. When disabled, the elapsed time counter
|
||||
* is not updated during update() calls, effectively pausing the
|
||||
* timeout detection without losing the current elapsed time state.
|
||||
*
|
||||
* Use cases for disabling:
|
||||
* - Temporary suspension during system operations
|
||||
* - Context-sensitive monitoring (disable in certain application states)
|
||||
* - Power management control (disable during low-power modes)
|
||||
* - User preference settings (allow users to disable timeouts)
|
||||
* - Development and debugging (disable for testing)
|
||||
*
|
||||
* When re-enabled, monitoring resumes from the current elapsed time state,
|
||||
* allowing for seamless pause/resume functionality.
|
||||
*
|
||||
* @post Monitoring state is updated according to the enabled parameter
|
||||
*
|
||||
* @note Disabling the tracker does not reset the elapsed time counter.
|
||||
* @note The timeout callback will not be executed while disabled, even
|
||||
* if the timeout threshold would otherwise be exceeded.
|
||||
* @note Enabling/disabling can be done at any time during operation.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* tracker.setEnabled(false); // Pause monitoring during critical operation
|
||||
* performCriticalOperation();
|
||||
* tracker.setEnabled(true); // Resume monitoring after completion
|
||||
* @endcode
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
private:
|
||||
uint64_t m_timeoutMs; ///< Timeout duration in milliseconds before callback execution
|
||||
uint64_t m_elapsedTime; ///< Current elapsed time since last reset in milliseconds
|
||||
bool m_enabled; ///< Flag indicating whether monitoring is currently active
|
||||
std::function<void()> m_onTimeout; ///< Callback function executed when timeout threshold is reached
|
||||
};
|
||||
223
firmware/components/insa/include/common/Menu.h
Normal file
223
firmware/components/insa/include/common/Menu.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @file Menu.h
|
||||
* @brief Menu widget class for creating interactive menu systems
|
||||
* @details This header defines the Menu class which extends the Widget base class
|
||||
* to provide a comprehensive, customizable menu system supporting various
|
||||
* types of interactive menu items including text buttons, selections,
|
||||
* number inputs, and toggles. The menu supports navigation with directional
|
||||
* input and provides visual feedback through selection highlighting and scrollbars.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "MenuOptions.h"
|
||||
#include "Widget.h"
|
||||
#include "data/MenuItem.h"
|
||||
|
||||
/**
|
||||
* @class Menu
|
||||
* @brief A comprehensive menu widget class for interactive user interfaces
|
||||
* @details This class extends the Widget base class to provide a customizable menu system
|
||||
* with support for various types of interactive menu items. It handles user input
|
||||
* through directional navigation and action buttons, provides visual feedback
|
||||
* through selection highlighting, and supports scrolling for long menu lists.
|
||||
*
|
||||
* The menu system supports four types of menu items:
|
||||
* - Text buttons: Simple selectable text items
|
||||
* - Selection items: Dropdown/list selection with multiple options
|
||||
* - Number inputs: Numeric value adjustment controls
|
||||
* - Toggle items: Boolean on/off switches
|
||||
*
|
||||
* @note Menu items are identified by unique IDs and can be dynamically added
|
||||
* after menu creation.
|
||||
*
|
||||
* @see Widget
|
||||
* @see MenuItem
|
||||
* @see menu_options_t
|
||||
*/
|
||||
class Menu : public Widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new Menu instance with the specified configuration
|
||||
* @param options Pointer to menu configuration options structure
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the menu's lifetime
|
||||
* @post Menu is initialized with the provided configuration and ready for item addition
|
||||
*
|
||||
* @note The menu does not take ownership of the options structure and assumes
|
||||
* it remains valid throughout the menu's lifetime.
|
||||
*/
|
||||
explicit Menu(menu_options_t *options);
|
||||
|
||||
/**
|
||||
* @brief Destructor - Cleans up resources when menu is destroyed
|
||||
* @details Properly releases any allocated resources and ensures clean shutdown
|
||||
* of the menu system.
|
||||
*/
|
||||
~Menu() override;
|
||||
|
||||
/**
|
||||
* @brief Adds a text-based menu item (button) to the menu
|
||||
* @param id Unique identifier for this menu item (must be unique within the menu)
|
||||
* @param text Display text shown on the menu item
|
||||
*
|
||||
* @pre id must be unique within this menu instance
|
||||
* @post A new text menu item is added to the menu's item collection
|
||||
*
|
||||
* @note Text items act as buttons and generate selection events when activated
|
||||
*/
|
||||
void addText(uint8_t id, const std::string &text);
|
||||
|
||||
void addTextCounter(uint8_t id, const std::string &text, const uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Adds a selection menu item (dropdown/list selection) to the menu
|
||||
* @param id Unique identifier for this menu item (must be unique within the menu)
|
||||
* @param text Display text/label for the selection item
|
||||
* @param values Vector of all available options to choose from
|
||||
* @param index Reference to current selected value (will be modified by user interaction)
|
||||
*
|
||||
* @pre id must be unique within this menu instance
|
||||
* @pre values vector must not be empty
|
||||
* @pre value must be one of the strings in the values vector
|
||||
* @post A new selection menu item is added with the specified options
|
||||
*
|
||||
* @note The value parameter is modified directly when the user changes the selection
|
||||
*/
|
||||
void addSelection(uint8_t id, const std::string &text, const std::vector<std::string> &values, int index);
|
||||
|
||||
/**
|
||||
* @brief Adds a toggle/checkbox menu item to the menu
|
||||
* @param id Unique identifier for this menu item (must be unique within the menu)
|
||||
* @param text Display text/label for the toggle item
|
||||
* @param selected Current state of the toggle (true = on/enabled, false = off/disabled)
|
||||
*
|
||||
* @pre id must be unique within this menu instance
|
||||
* @post A new toggle menu item is added with the specified initial state
|
||||
*
|
||||
* @note Toggle state can be changed through user interaction with select button
|
||||
*/
|
||||
void addToggle(uint8_t id, const std::string &text, bool selected);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Virtual callback method for handling button press events on specific menu items
|
||||
* @param item The menu item that received the button press
|
||||
* @param button The type of button that was pressed
|
||||
*
|
||||
* @details This method can be overridden by derived classes to implement custom
|
||||
* button handling logic for specific menu items. The base implementation
|
||||
* is empty, allowing derived classes to selectively handle events.
|
||||
*
|
||||
* @note Override this method in derived classes to implement custom menu item
|
||||
* interaction behavior beyond the standard navigation and value modification.
|
||||
*/
|
||||
virtual void onButtonPressed(const MenuItem &item, const ButtonType button)
|
||||
{
|
||||
// Base implementation intentionally empty - override in derived classes as needed
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves a menu item by its index position
|
||||
* @param index Zero-based index of the menu item to retrieve
|
||||
* @return MenuItem object at the specified index
|
||||
*
|
||||
* @pre index must be within valid range [0, getItemCount()-1]
|
||||
* @post Returns a copy of the menu item at the specified position
|
||||
*
|
||||
* @throws std::out_of_range if index is invalid
|
||||
*
|
||||
* @note This method returns a copy of the menu item, not a reference
|
||||
*/
|
||||
MenuItem getItem(int index);
|
||||
|
||||
/**
|
||||
* @brief Gets the total number of menu items in the menu
|
||||
* @return Size of the menu items collection
|
||||
*
|
||||
* @post Returns current count of menu items (>= 0)
|
||||
*
|
||||
* @note This count includes all types of menu items (text, selection, toggle)
|
||||
*/
|
||||
[[nodiscard]] size_t getItemCount() const;
|
||||
|
||||
/**
|
||||
* @brief Dynamically adjusts the number of menu items to the specified size
|
||||
* @param size Target number of menu items the menu should contain
|
||||
*
|
||||
* @details If the target size is larger than current item count, new selection
|
||||
* items are added using the first item's values as template. If the
|
||||
* target size is smaller, excess items are removed from the end.
|
||||
*
|
||||
* @pre size must be > 0 and at least one menu item must exist as template
|
||||
* @post Menu contains exactly 'size' number of items
|
||||
*
|
||||
* @note New items are created as selection items with auto-generated names
|
||||
* in the format "Section X" where X is the item number
|
||||
*/
|
||||
void setItemSize(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Toggles the boolean state of a toggle menu item
|
||||
* @param menuItem The toggle menu item whose state should be flipped
|
||||
*
|
||||
* @pre menuItem must be of type TOGGLE
|
||||
* @post The menu item's value is switched between "true" and "false"
|
||||
*
|
||||
* @details Changes "true" to "false" and "false" to "true" for toggle items.
|
||||
* The modified item replaces the original in the menu's item collection.
|
||||
*
|
||||
* @note This method directly modifies the menu's internal state
|
||||
*/
|
||||
void toggle(const MenuItem &menuItem);
|
||||
|
||||
/**
|
||||
* @brief Changes the selected value of a selection menu item based on button input
|
||||
* @param menuItem The selection menu item to modify
|
||||
* @param button The directional button pressed (LEFT or RIGHT)
|
||||
*
|
||||
* @pre menuItem must be of type SELECTION with valid values array
|
||||
* @post The menu item's selected index is adjusted based on button direction
|
||||
*
|
||||
* @details LEFT button moves to previous option (wraps to end if at beginning),
|
||||
* RIGHT button moves to next option (wraps to beginning if at end).
|
||||
* Other button types are ignored.
|
||||
*
|
||||
* @note The modified item replaces the original in the menu's item collection
|
||||
*/
|
||||
MenuItem switchValue(const MenuItem &menuItem, ButtonType button);
|
||||
|
||||
private:
|
||||
MenuItem replaceItem(int index, const MenuItem &item);
|
||||
|
||||
void render() override;
|
||||
|
||||
void onButtonClicked(ButtonType button) override;
|
||||
|
||||
void onPressedDown();
|
||||
|
||||
void onPressedUp();
|
||||
|
||||
void onPressedLeft() const;
|
||||
|
||||
void onPressedRight() const;
|
||||
|
||||
void onPressedSelect() const;
|
||||
|
||||
void onPressedBack() const;
|
||||
|
||||
void drawScrollBar() const;
|
||||
|
||||
void drawSelectionBox() const;
|
||||
|
||||
void renderWidget(const MenuItem *item, const uint8_t *font, int x, int y) const;
|
||||
|
||||
// Member variables
|
||||
size_t m_selected_item = 0;
|
||||
std::vector<MenuItem> m_items;
|
||||
menu_options_t *m_options;
|
||||
};
|
||||
106
firmware/components/insa/include/common/ScrollBar.h
Normal file
106
firmware/components/insa/include/common/ScrollBar.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @file ScrollBar.h
|
||||
* @brief Vertical scrollbar widget for indicating scroll position in long content
|
||||
* @details This header defines the ScrollBar class which provides a visual scrollbar
|
||||
* widget for indicating the current position within scrollable content.
|
||||
* The scrollbar displays a thumb that moves proportionally to represent
|
||||
* the current scroll position and visible area relative to the total content.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MenuOptions.h"
|
||||
#include "Widget.h"
|
||||
|
||||
/**
|
||||
* @class ScrollBar
|
||||
* @brief A vertical scrollbar widget that represents scroll position and range
|
||||
* @details This final class inherits from Widget and provides visual feedback for
|
||||
* scrollable content. It displays a vertical track with a movable thumb
|
||||
* that indicates the current position within a scrollable range. The thumb
|
||||
* size is proportional to the visible area relative to the total content,
|
||||
* and its position reflects the current scroll offset.
|
||||
*
|
||||
* The scrollbar automatically calculates thumb dimensions and position based on
|
||||
* the provided scroll values (current, minimum, maximum). It is designed to be
|
||||
* used alongside scrollable content like menus or lists to provide visual
|
||||
* feedback about scroll state.
|
||||
*
|
||||
* @note This class is marked as final and cannot be inherited from.
|
||||
*
|
||||
* @see Widget
|
||||
* @see menu_options_t
|
||||
*/
|
||||
class ScrollBar final : public Widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a ScrollBar with specified position and dimensions
|
||||
* @param options Pointer to menu options configuration structure
|
||||
* @param x X coordinate position of the scrollbar on screen
|
||||
* @param y Y coordinate position of the scrollbar on screen
|
||||
* @param width Width of the scrollbar in pixels
|
||||
* @param height Height of the scrollbar in pixels
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the scrollbar's lifetime
|
||||
* @pre width and height must be greater than 0
|
||||
* @pre x and y must be valid screen coordinates
|
||||
* @post ScrollBar is initialized with the specified geometry and ready for use
|
||||
*
|
||||
* @note The scrollbar does not take ownership of the options structure and
|
||||
* assumes it remains valid throughout the scrollbar's lifetime.
|
||||
*/
|
||||
ScrollBar(const menu_options_t *options, size_t x, size_t y, size_t width, size_t height);
|
||||
|
||||
/**
|
||||
* @brief Renders the scrollbar to the screen
|
||||
* @details Overrides the base Widget render method to draw the scrollbar track
|
||||
* and thumb. The appearance is determined by the current scroll state
|
||||
* and the menu options configuration.
|
||||
*
|
||||
* @pre u8g2 display context must be initialized and ready for drawing
|
||||
* @post Scrollbar's visual representation is drawn to the display buffer
|
||||
*
|
||||
* @note This method is called during each frame's render cycle. The scrollbar
|
||||
* track and thumb are drawn based on the current scroll values set by refresh().
|
||||
*/
|
||||
void render() override;
|
||||
|
||||
/**
|
||||
* @brief Updates the scrollbar state with new scroll values
|
||||
* @param value Current scroll position value (must be between min and max)
|
||||
* @param max Maximum scroll value (total content size)
|
||||
* @param min Minimum scroll value (default: 0, typically the start of content)
|
||||
*
|
||||
* @pre value must be between min and max (inclusive)
|
||||
* @pre max must be greater than or equal to min
|
||||
* @post Scrollbar thumb position and size are recalculated based on new values
|
||||
*
|
||||
* @details This method recalculates the thumb's height and vertical position
|
||||
* based on the provided scroll range and current position. The thumb
|
||||
* height represents the proportion of visible content to total content,
|
||||
* while its position represents the current scroll offset within the range.
|
||||
*
|
||||
* @note Call this method whenever the scroll state changes to keep the
|
||||
* scrollbar visualization synchronized with the actual content position.
|
||||
*/
|
||||
void refresh(size_t value, size_t max, size_t min = 0);
|
||||
|
||||
private:
|
||||
// Position and dimensions
|
||||
size_t m_x; ///< X coordinate of the scrollbar's left edge
|
||||
size_t m_y; ///< Y coordinate of the scrollbar's top edge
|
||||
size_t m_width; ///< Width of the scrollbar track in pixels
|
||||
size_t m_height; ///< Height of the scrollbar track in pixels
|
||||
|
||||
// Scroll state values
|
||||
size_t m_value; ///< Current scroll position within the range [m_min, m_max]
|
||||
size_t m_max; ///< Maximum scroll value representing the end of content
|
||||
size_t m_min; ///< Minimum scroll value representing the start of content
|
||||
|
||||
// Calculated thumb properties (updated by refresh())
|
||||
size_t m_thumbHeight; ///< Calculated height of the scroll thumb in pixels
|
||||
size_t m_thumbY; ///< Calculated Y position of the scroll thumb relative to track
|
||||
};
|
||||
168
firmware/components/insa/include/common/Widget.h
Normal file
168
firmware/components/insa/include/common/Widget.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @file Widget.h
|
||||
* @brief Base widget class for UI components in the INSA system
|
||||
* @details This header defines the Widget base class that serves as the foundation
|
||||
* for all UI components in the system. It provides a standardized interface
|
||||
* for rendering, updating, and handling user input using the u8g2 graphics library.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "u8g2.h"
|
||||
|
||||
#include "common/Common.h"
|
||||
|
||||
/**
|
||||
* @class Widget
|
||||
* @brief Base class for UI widgets that can be rendered and interact with user input
|
||||
* @details This abstract base class provides a common interface for all widgets in the system.
|
||||
* It manages the u8g2 display context and defines the core methods that all widgets
|
||||
* must implement or can override. The class follows the template method pattern,
|
||||
* allowing derived classes to customize behavior while maintaining a consistent
|
||||
* interface for the UI system.
|
||||
*
|
||||
* @note All widgets should inherit from this class to ensure compatibility with
|
||||
* the UI management system.
|
||||
*
|
||||
* @see u8g2_t
|
||||
* @see ButtonType
|
||||
*/
|
||||
class Widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a widget with the given u8g2 display context
|
||||
* @param u8g2 Pointer to the u8g2 display context used for rendering operations
|
||||
*
|
||||
* @pre u8g2 must not be nullptr
|
||||
* @post Widget is initialized with the provided display context
|
||||
*
|
||||
* @note The widget does not take ownership of the u8g2 context and assumes
|
||||
* it remains valid for the lifetime of the widget.
|
||||
*/
|
||||
explicit Widget(u8g2_t *u8g2);
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor to ensure proper cleanup of derived classes
|
||||
* @details Ensures that derived class destructors are called correctly when
|
||||
* a widget is destroyed through a base class pointer.
|
||||
*/
|
||||
virtual ~Widget() = default;
|
||||
|
||||
/**
|
||||
* @brief Called when the widget becomes active or enters the foreground
|
||||
* @details This method is invoked when the widget transitions from inactive
|
||||
* to active state, such as when it becomes the current screen or
|
||||
* gains focus. Derived classes can override this method to perform
|
||||
* initialization tasks, reset state, or prepare for user interaction.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if entry behavior is needed.
|
||||
* @note This method is typically called by the UI management system during
|
||||
* screen transitions or focus changes.
|
||||
*/
|
||||
virtual void enter();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget is temporarily paused or loses focus
|
||||
* @details This method is invoked when the widget needs to suspend its
|
||||
* operations temporarily, such as when another widget takes focus
|
||||
* or the system enters a paused state. Derived classes can override
|
||||
* this method to pause animations, save state, or reduce resource usage.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if pause behavior is needed.
|
||||
* @note The widget should be prepared to resume from this state when resume() is called.
|
||||
*/
|
||||
virtual void pause();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget resumes from a paused state
|
||||
* @details This method is invoked when the widget transitions from paused
|
||||
* to active state, typically after a previous pause() call. Derived
|
||||
* classes can override this method to restore animations, reload
|
||||
* resources, or continue interrupted operations.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if resume behavior is needed.
|
||||
* @note This method should restore the widget to the state it was in before pause() was called.
|
||||
*/
|
||||
virtual void resume();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget is being destroyed or exits the system
|
||||
* @details This method is invoked when the widget is about to be removed
|
||||
* from the system or transitions to an inactive state permanently.
|
||||
* Derived classes can override this method to perform cleanup tasks,
|
||||
* save final state, or release resources that are not automatically freed.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if exit behavior is needed.
|
||||
* @note This method is called before the widget's destructor and provides
|
||||
* an opportunity for controlled shutdown of widget-specific resources.
|
||||
*/
|
||||
virtual void exit();
|
||||
|
||||
/**
|
||||
* @brief Updates the widget's internal state based on elapsed time
|
||||
* @param dt Delta time in milliseconds since the last update call
|
||||
*
|
||||
* @details This method is called once per frame by the UI system to handle
|
||||
* animations, timers, state transitions, or other time-dependent behavior.
|
||||
* The base implementation is empty, allowing derived classes to override
|
||||
* only if time-based updates are needed.
|
||||
*
|
||||
* @note Override this method in derived classes to implement time-based behavior
|
||||
* such as animations, blinking effects, or timeout handling.
|
||||
*/
|
||||
virtual void update(uint64_t dt);
|
||||
|
||||
/**
|
||||
* @brief Renders the widget to the display
|
||||
* @details This method should be overridden by derived classes to implement
|
||||
* their specific rendering logic using the u8g2 display context.
|
||||
* The base implementation is empty, requiring derived classes to
|
||||
* provide their own rendering code.
|
||||
*
|
||||
* @pre u8g2 context must be initialized and ready for drawing operations
|
||||
* @post Widget's visual representation is drawn to the display buffer
|
||||
*
|
||||
* @note This method is called during the rendering phase of each frame.
|
||||
* Derived classes should use the u8g2 member variable to perform
|
||||
* drawing operations.
|
||||
*/
|
||||
virtual void render();
|
||||
|
||||
/**
|
||||
* @brief Handles button press events
|
||||
* @param button The type of button that was pressed
|
||||
*
|
||||
* @details This method is called when a button press event occurs and allows
|
||||
* the widget to respond to user input. The base implementation is empty,
|
||||
* allowing derived classes to override only if input handling is needed.
|
||||
*
|
||||
* @note Override this method in derived classes to implement button-specific
|
||||
* behavior such as navigation, selection, or state changes.
|
||||
*
|
||||
* @see ButtonType for available button types
|
||||
*/
|
||||
virtual void onButtonClicked(ButtonType button);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Pointer to the u8g2 display context used for rendering operations
|
||||
* @details This member provides access to the u8g2 graphics library functions
|
||||
* for drawing text, shapes, bitmaps, and other UI elements. It is
|
||||
* initialized during construction and remains valid for the widget's lifetime.
|
||||
*
|
||||
* @note This member is protected to allow derived classes direct access while
|
||||
* preventing external modification. Derived classes should use this
|
||||
* context for all rendering operations.
|
||||
*
|
||||
* @warning Do not modify or delete this pointer. The widget does not own
|
||||
* the u8g2 context and assumes it is managed externally.
|
||||
*/
|
||||
u8g2_t *u8g2;
|
||||
};
|
||||
310
firmware/components/insa/include/data/MenuItem.h
Normal file
310
firmware/components/insa/include/data/MenuItem.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* @file MenuItem.h
|
||||
* @brief Menu item data structure for user interface menu systems
|
||||
* @details This header defines the MenuItem class which represents individual menu
|
||||
* items that can be displayed and interacted with in user interface menus.
|
||||
* It supports various types of menu items including simple buttons, toggles,
|
||||
* value selectors, and multi-option selections with flexible callback handling.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/Common.h"
|
||||
|
||||
/**
|
||||
* @class MenuItem
|
||||
* @brief Flexible menu item class supporting various interaction types and behaviors
|
||||
* @details This class represents individual menu items that can be displayed in user
|
||||
* interface menus. It provides a flexible foundation for different types of
|
||||
* menu interactions including simple navigation buttons, toggle switches,
|
||||
* value adjustments, and multi-option selections.
|
||||
*
|
||||
* The MenuItem class supports multiple interaction patterns:
|
||||
* - **Simple Actions**: Basic menu items that execute a function when activated
|
||||
* - **Value Display**: Items that show a current value (read-only or editable)
|
||||
* - **Selection Lists**: Items that cycle through multiple predefined values
|
||||
* - **Toggle States**: Boolean items that can be switched on/off
|
||||
* - **Custom Behaviors**: Flexible callback system for specialized interactions
|
||||
*
|
||||
* Each menu item is identified by a unique ID and has a type that defines its
|
||||
* visual appearance and interaction behavior. The callback system allows for
|
||||
* flexible event handling while maintaining type safety through std::function.
|
||||
*
|
||||
* Key features include:
|
||||
* - Multiple constructor overloads for different menu item types
|
||||
* - Type-safe callback system with ButtonCallback function objects
|
||||
* - Support for both single values and value lists
|
||||
* - Flexible text and value management
|
||||
* - Efficient state management and validation
|
||||
*
|
||||
* @note This class is designed to be lightweight and efficient for embedded
|
||||
* systems while providing rich functionality for complex user interfaces.
|
||||
*
|
||||
* @see ButtonCallback for callback function signature
|
||||
* @see ButtonType for available button types
|
||||
* @see Menu for menu container functionality
|
||||
*/
|
||||
class MenuItem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a simple action menu item with text and callback
|
||||
* @param id Unique identifier for this menu item within its parent menu
|
||||
* @param type Type identifier defining the item's behavior and visual appearance
|
||||
* @param text Display text shown to the user for this menu item
|
||||
* @param callback Function to call when the item is activated
|
||||
*
|
||||
* @pre id must be unique within the parent menu context
|
||||
* @pre text should not be empty for proper user interface display
|
||||
* @pre callback should be a valid callable object
|
||||
* @post MenuItem is initialized as a simple action item ready for display
|
||||
*
|
||||
* @details Creates a basic menu item that displays text and executes a callback
|
||||
* when activated. This is the most common type of menu item used for
|
||||
* navigation, simple actions, and command execution.
|
||||
*
|
||||
* Typical use cases include:
|
||||
* - Navigation items (e.g., "Settings", "Back", "Exit")
|
||||
* - Action items (e.g., "Save", "Reset", "Start")
|
||||
* - Sub-menu entries (e.g., "Light Control", "System Info")
|
||||
*
|
||||
* @note The callback is stored as a std::function and can be a lambda,
|
||||
* function pointer, or any callable object matching the ButtonCallback signature.
|
||||
*/
|
||||
MenuItem(uint8_t id, uint8_t type, std::string text, ButtonCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Constructs a value-displaying menu item with text, value, and callback
|
||||
* @param id Unique identifier for this menu item within its parent menu
|
||||
* @param type Type identifier defining the item's behavior and visual appearance
|
||||
* @param text Display text shown to the user for this menu item
|
||||
* @param value Current value associated with this item (displayed to user)
|
||||
* @param callback Function to call when the item is activated
|
||||
*
|
||||
* @pre id must be unique within the parent menu context
|
||||
* @pre text should not be empty for proper user interface display
|
||||
* @pre callback should be a valid callable object
|
||||
* @post MenuItem is initialized with text and value display capabilities
|
||||
*
|
||||
* @details Creates a menu item that displays both text and a current value.
|
||||
* This type is commonly used for settings display, status information,
|
||||
* or items where the current state needs to be visible to the user.
|
||||
*
|
||||
* Typical use cases include:
|
||||
* - Setting displays (e.g., "Brightness: 75%")
|
||||
* - Status information (e.g., "Connection: WiFi")
|
||||
* - Editable values (e.g., "Timeout: 30s")
|
||||
* - Current selections (e.g., "Mode: Auto")
|
||||
*
|
||||
* @note The value can be updated later using setValue() to reflect changes
|
||||
* in the underlying system state.
|
||||
*/
|
||||
MenuItem(uint8_t id, uint8_t type, std::string text, std::string value, ButtonCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Constructs a multi-selection menu item with selectable values
|
||||
* @param id Unique identifier for this menu item within its parent menu
|
||||
* @param type Type identifier defining the item's behavior and visual appearance
|
||||
* @param text Display text shown to the user for this menu item
|
||||
* @param values List of all available values that can be selected
|
||||
* @param index Currently selected value from the available options
|
||||
* @param callback Function to call when the item is activated
|
||||
*
|
||||
* @pre id must be unique within the parent menu context
|
||||
* @pre text should not be empty for proper user interface display
|
||||
* @pre value should be present in the values vector
|
||||
* @pre values should not be empty and should contain valid options
|
||||
* @pre callback should be a valid callable object
|
||||
* @post MenuItem is initialized with multiple selectable values
|
||||
*
|
||||
* @details Creates a menu item that allows selection from multiple predefined
|
||||
* values. This type enables cycling through options or displaying
|
||||
* selection dialogs, making it ideal for configuration settings
|
||||
* with discrete choices.
|
||||
*
|
||||
* Typical use cases include:
|
||||
* - Mode selection (e.g., "Display Mode: [Day, Night, Auto]")
|
||||
* - Configuration options (e.g., "Language: [English, Deutsch, Français]")
|
||||
* - Preset selection (e.g., "Profile: [Home, Office, Travel]")
|
||||
* - Format selection (e.g., "Time Format: [12H, 24H]")
|
||||
*
|
||||
* @note The callback can implement cycling logic to move through the values
|
||||
* or open a selection dialog for user choice.
|
||||
* @note The values vector is stored by copy, so modifications to the original
|
||||
* vector after construction do not affect the menu item.
|
||||
*/
|
||||
MenuItem(uint8_t id, uint8_t type, std::string text, std::vector<std::string> values, int index,
|
||||
ButtonCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Gets the unique identifier of this menu item
|
||||
* @return The menu item's unique ID as assigned during construction
|
||||
*
|
||||
* @details Returns the unique identifier that distinguishes this menu item
|
||||
* from others within the same menu context. This ID is used by
|
||||
* menu systems for item identification, event routing, and state management.
|
||||
*
|
||||
* @note The ID is immutable after construction and guaranteed to be unique
|
||||
* within the menu context where this item is used.
|
||||
*/
|
||||
[[nodiscard]] uint8_t getId() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type identifier of this menu item
|
||||
* @return The menu item's type identifier defining its behavior
|
||||
*
|
||||
* @details Returns the type identifier that defines how this menu item
|
||||
* behaves and appears in the user interface. The type determines
|
||||
* rendering style, interaction patterns, and event handling behavior.
|
||||
*
|
||||
* @note The type is immutable after construction and should correspond
|
||||
* to predefined menu item type constants defined in the system.
|
||||
*/
|
||||
[[nodiscard]] uint8_t getType() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the display text of this menu item
|
||||
* @return Const reference to the menu item's display text
|
||||
*
|
||||
* @details Returns the text that is displayed to the user for this menu item.
|
||||
* This text serves as the primary label and should be descriptive
|
||||
* enough for users to understand the item's purpose.
|
||||
*
|
||||
* @note Returns a const reference for efficiency while preventing
|
||||
* accidental modification of the text content.
|
||||
*/
|
||||
[[nodiscard]] const std::string &getText() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the current value of this menu item
|
||||
* @return Const reference to the menu item's current value
|
||||
*
|
||||
* @details Returns the current value associated with this menu item.
|
||||
* For simple action items, this may be empty. For value-based
|
||||
* items, this represents the current state, selection, or setting.
|
||||
*
|
||||
* @note Returns a const reference for efficiency while preventing
|
||||
* accidental modification through the getter.
|
||||
* @note For boolean items, the value is typically "true"/"false" or similar.
|
||||
*/
|
||||
[[nodiscard]] const std::string &getValue() const;
|
||||
|
||||
/**
|
||||
* @brief Sets a new value for this menu item
|
||||
* @param value The new value to assign to this menu item
|
||||
*
|
||||
* @details Updates the current value of this menu item. This is commonly
|
||||
* used to reflect changes in system state, user selections, or
|
||||
* configuration updates. The new value should be appropriate
|
||||
* for the menu item's type and purpose.
|
||||
*
|
||||
* @note For multi-selection items, the value should be one of the
|
||||
* predefined values in the values vector.
|
||||
* @note For boolean items, typical values are "true"/"false", "ON"/"OFF",
|
||||
* or other boolean representations.
|
||||
* @note The value update does not automatically trigger the callback;
|
||||
* this is purely for state management.
|
||||
*/
|
||||
void setValue(const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief Handles button press events for this menu item
|
||||
* @param button The type of button that was pressed
|
||||
*
|
||||
* @details Processes button press events by invoking the associated callback
|
||||
* function if one exists. This method serves as the event handler
|
||||
* that connects user interactions to the menu item's functionality.
|
||||
*
|
||||
* The method performs the following actions:
|
||||
* - Validates that the ID matches this menu item
|
||||
* - Checks if a callback function is available
|
||||
* - Invokes the callback with the provided button type
|
||||
* - Handles any callback-related error conditions gracefully
|
||||
*
|
||||
* @note This method is typically called by the parent menu system when
|
||||
* user interaction occurs on this menu item.
|
||||
* @note If no callback is set, the method returns without error.
|
||||
* @note The callback is responsible for implementing the specific behavior
|
||||
* for the button press (navigation, value changes, actions, etc.).
|
||||
*/
|
||||
void onButtonPressed(ButtonType button) const;
|
||||
|
||||
/**
|
||||
* @brief Checks if this menu item has an associated callback function
|
||||
* @return true if a callback function is set, false otherwise
|
||||
*
|
||||
* @details Determines whether this menu item has a valid callback function
|
||||
* that can be invoked when the item is activated. This is useful
|
||||
* for menu systems that need to distinguish between interactive
|
||||
* and non-interactive items.
|
||||
*
|
||||
* @note Menu items without callbacks are typically used for display-only
|
||||
* purposes such as headers, separators, or status information.
|
||||
* @note Interactive menu items should always have callbacks to provide
|
||||
* meaningful user interaction.
|
||||
*/
|
||||
[[nodiscard]] bool hasCallback() const;
|
||||
|
||||
[[nodiscard]] int getIndex() const;
|
||||
|
||||
[[nodiscard]] std::vector<std::string> getValues() const;
|
||||
|
||||
[[nodiscard]] size_t getItemCount() const;
|
||||
|
||||
[[nodiscard]] MenuItem copyWith(const std::string &value) const;
|
||||
|
||||
[[nodiscard]] MenuItem copyWith(size_t index) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Unique identifier for this menu item
|
||||
* @details Stores the unique ID that distinguishes this menu item from others
|
||||
* within the same menu context. Used for event routing and item identification.
|
||||
*/
|
||||
uint8_t m_id;
|
||||
|
||||
/**
|
||||
* @brief Type identifier defining the item's behavior and appearance
|
||||
* @details Stores the type that determines how this menu item behaves,
|
||||
* how it's rendered, and what interaction patterns it supports.
|
||||
*/
|
||||
uint8_t m_type;
|
||||
|
||||
/**
|
||||
* @brief Display text shown to the user
|
||||
* @details Stores the primary text label that is displayed to users,
|
||||
* serving as the main identifier and description of the menu item's purpose.
|
||||
*/
|
||||
std::string m_text;
|
||||
|
||||
/**
|
||||
* @brief Current value associated with this menu item
|
||||
* @details Stores the current value for value-based menu items, representing
|
||||
* the current state, selection, or setting that should be displayed to the user.
|
||||
*/
|
||||
std::string m_value;
|
||||
|
||||
/**
|
||||
* @brief Available values for selection-based menu items
|
||||
* @details Stores the list of all possible values that can be selected for
|
||||
* multi-option menu items. Used for cycling through options or
|
||||
* displaying selection dialogs.
|
||||
*/
|
||||
std::vector<std::string> m_values;
|
||||
|
||||
int m_index = -1;
|
||||
|
||||
/**
|
||||
* @brief Callback function invoked when the menu item is activated
|
||||
* @details Stores the function object that implements the menu item's behavior
|
||||
* when activated by user interaction. Uses std::function for flexibility
|
||||
* and type safety.
|
||||
*/
|
||||
ButtonCallback m_callback;
|
||||
};
|
||||
23
firmware/components/insa/include/data/roads.h
Normal file
23
firmware/components/insa/include/data/roads.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#define road_horizontal_width 16
|
||||
#define road_horizontal_height 16
|
||||
static unsigned char road_horizontal_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x8c, 0xc7, 0x8c, 0xc7, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_vertical_width 16
|
||||
#define road_vertical_height 16
|
||||
static unsigned char road_vertical_bits[] = {0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_t_up_width 16
|
||||
#define road_t_up_height 16
|
||||
static unsigned char road_t_up_bits[] = {0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||
0x03, 0xcc, 0xcf, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_t_crossing_width 16
|
||||
#define road_t_crossing_height 16
|
||||
static unsigned char road_t_crossing_bits[] = {0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x80,
|
||||
0x01, 0xe6, 0x67, 0xe6, 0x67, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
55
firmware/components/insa/include/data/vehicles.h
Normal file
55
firmware/components/insa/include/data/vehicles.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#define car_width 16
|
||||
#define car_height 16
|
||||
const unsigned char car_left_bits [] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee, 0x77, 0xee, 0x77,
|
||||
0x2e, 0x74, 0xde, 0x78, 0x02, 0x40, 0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00
|
||||
};
|
||||
const unsigned char car_right_bits [] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee, 0x77, 0xee, 0x77,
|
||||
0x2e, 0x74, 0x1e, 0x7b, 0x02, 0x40, 0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00
|
||||
};
|
||||
|
||||
#define convertable_width 16
|
||||
#define convertable_height 16
|
||||
static unsigned char convertable_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x0c, 0xee,
|
||||
0x77, 0x2e, 0x74, 0xae, 0x75, 0xae, 0x75, 0xfe, 0x7f, 0x82, 0x41,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
static unsigned char convertable_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x0c, 0xee,
|
||||
0x77, 0x2e, 0x74, 0xae, 0x75, 0xae, 0x75, 0xfe, 0x7f, 0x82, 0x41,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
|
||||
#define lorry_width 32
|
||||
#define lorry_height 16
|
||||
const unsigned char lorry_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xe0,
|
||||
0x0b, 0x00, 0x00, 0xf0, 0x0b, 0x00, 0x00, 0xee, 0x0b, 0x00, 0x00, 0xee, 0x0f,
|
||||
0xbc, 0x07, 0x2e, 0xfa, 0xff, 0x7f, 0xae, 0xfa, 0xff, 0x7f, 0xbe, 0x0a, 0x00,
|
||||
0x60, 0x02, 0x0a, 0x00, 0x60, 0x3c, 0x0a, 0xbc, 0x67, 0x42, 0xfe, 0x43, 0x78,
|
||||
0xda, 0xff, 0x5b, 0x7b, 0x18, 0x00, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned char lorry_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
|
||||
0x00, 0xd0, 0x07, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xd0, 0x77, 0xe0, 0x3d,
|
||||
0xf0, 0x77, 0xfe, 0xff, 0x5f, 0x74, 0xfe, 0xff, 0x5f, 0x75, 0x06, 0x00, 0x50,
|
||||
0x7d, 0x06, 0x00, 0x50, 0x40, 0xe6, 0x3d, 0x50, 0x3c, 0x1e, 0xc2, 0x7f, 0x42,
|
||||
0xde, 0xda, 0xff, 0x5b, 0xc0, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00};
|
||||
#define suv_width 16
|
||||
#define suv_height 16
|
||||
const unsigned char suv_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee,
|
||||
0x77, 0xee, 0x77, 0x2e, 0x74, 0xde, 0x78, 0x02, 0x40, 0x02, 0x40,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
const unsigned char suv_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee,
|
||||
0x77, 0xee, 0x77, 0x2e, 0x74, 0x1e, 0x7b, 0x02, 0x40, 0x02, 0x40,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
|
||||
#define truck_width 32
|
||||
#define truck_height 16
|
||||
const unsigned char truck_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x00, 0xff, 0xff, 0x7f, 0xe0,
|
||||
0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0x7f, 0xee, 0xff, 0xff, 0x7f, 0xee, 0x03,
|
||||
0x00, 0x60, 0x2e, 0x02, 0x00, 0x40, 0xae, 0x02, 0x00, 0x40, 0xbe, 0x02, 0x00,
|
||||
0x40, 0x02, 0x02, 0x00, 0x40, 0x3c, 0x02, 0xbc, 0x47, 0x42, 0x02, 0x42, 0x48,
|
||||
0xda, 0xff, 0x5b, 0x7b, 0x18, 0x38, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned char truck_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0xfe, 0xff, 0xff, 0x00, 0xfe,
|
||||
0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x77, 0x06, 0x00,
|
||||
0xc0, 0x77, 0x02, 0x00, 0x40, 0x74, 0x02, 0x00, 0x40, 0x75, 0x02, 0x00, 0x40,
|
||||
0x7d, 0x02, 0x00, 0x40, 0x40, 0xe2, 0x3d, 0x40, 0x3c, 0x12, 0x42, 0x40, 0x42,
|
||||
0xde, 0xda, 0xff, 0x5b, 0xc0, 0x18, 0x1c, 0x18, 0x00, 0x00, 0x00, 0x00};
|
||||
141
firmware/components/insa/include/ui/LightMenu.h
Normal file
141
firmware/components/insa/include/ui/LightMenu.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @file LightMenu.h
|
||||
* @brief Light control menu implementation for lighting system management
|
||||
* @details This header defines the LightMenu class which provides a specialized
|
||||
* user interface for controlling lighting systems and illumination features.
|
||||
* It extends the Menu base class to offer comprehensive light control
|
||||
* functionality including brightness adjustment, color selection, and
|
||||
* lighting mode configuration.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
/**
|
||||
* @class LightMenu
|
||||
* @brief Light control menu interface class for illumination system management
|
||||
* @details This final class inherits from Menu and provides a comprehensive interface
|
||||
* for controlling various aspects of the lighting system. It allows users to
|
||||
* adjust brightness levels, select colors, configure lighting modes, and
|
||||
* manage automated lighting behaviors.
|
||||
*
|
||||
* The LightMenu class extends the base Menu functionality by:
|
||||
* - Providing light-specific control options (brightness, color, modes)
|
||||
* - Implementing real-time lighting preview and feedback
|
||||
* - Managing lighting presets and custom configurations
|
||||
* - Handling immediate lighting adjustments and scheduled operations
|
||||
*
|
||||
* Typical lighting control features include:
|
||||
* - Brightness adjustment (0-100% or similar range)
|
||||
* - Color selection (RGB, HSV, or predefined colors)
|
||||
* - Lighting modes (solid, fade, strobe, custom patterns)
|
||||
* - Timer-based automation (on/off schedules)
|
||||
* - Preset management (save/load favorite configurations)
|
||||
* - Zone-based control (if multiple light zones are supported)
|
||||
*
|
||||
* The menu provides immediate visual feedback by applying changes to the
|
||||
* connected lighting hardware in real-time as users navigate through options.
|
||||
*
|
||||
* @note This class is marked as final and cannot be inherited from.
|
||||
* @note Lighting changes are typically applied immediately for instant feedback,
|
||||
* with the option to save configurations as presets.
|
||||
*
|
||||
* @see Menu for base menu functionality
|
||||
* @see menu_options_t for configuration structure
|
||||
* @see MainMenu for navigation back to main interface
|
||||
*/
|
||||
class LightMenu final : public Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs the light control menu with the specified configuration
|
||||
* @param options Pointer to menu options configuration structure
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the menu's lifetime
|
||||
* @pre options->u8g2 must be initialized and ready for graphics operations
|
||||
* @pre All callback functions in options must be properly configured
|
||||
* @pre Lighting hardware must be initialized and responsive
|
||||
* @post LightMenu is initialized with current lighting state and ready for user interaction
|
||||
*
|
||||
* @details The constructor initializes the light control menu by:
|
||||
* - Reading current lighting system state and parameters
|
||||
* - Creating appropriate menu items for available lighting features
|
||||
* - Setting up real-time preview capabilities
|
||||
* - Loading saved lighting presets and configurations
|
||||
* - Configuring value ranges and validation for lighting parameters
|
||||
*
|
||||
* The menu automatically detects available lighting capabilities and presents
|
||||
* only the controls that are supported by the connected hardware. This ensures
|
||||
* a consistent user experience across different lighting system configurations.
|
||||
*
|
||||
* @note The menu does not take ownership of the options structure and assumes
|
||||
* it remains valid throughout the menu's lifetime.
|
||||
* @note Current lighting state is preserved and can be restored if the user
|
||||
* exits without saving changes.
|
||||
*
|
||||
* @see Menu::Menu for base class construction details
|
||||
*/
|
||||
explicit LightMenu(menu_options_t *options);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handles button press events specific to light control menu items
|
||||
* @param menuItem
|
||||
* @param button Type of button that was pressed
|
||||
*
|
||||
* @details Overrides the base Menu class method to provide light control-specific
|
||||
* button handling logic. This method processes user interactions with
|
||||
* lighting control items and performs appropriate actions such as:
|
||||
* - Adjusting brightness levels (increment/decrement)
|
||||
* - Changing color values or selecting predefined colors
|
||||
* - Switching between lighting modes and patterns
|
||||
* - Saving/loading lighting presets
|
||||
* - Toggling lighting zones on/off
|
||||
* - Applying lighting changes immediately to hardware
|
||||
*
|
||||
* The method provides real-time feedback by immediately applying lighting
|
||||
* changes to the connected hardware, allowing users to see the effects of
|
||||
* their adjustments instantly. It also handles validation to ensure that
|
||||
* lighting parameters remain within safe and supported ranges.
|
||||
*
|
||||
* Special handling includes:
|
||||
* - Smooth transitions for brightness adjustments
|
||||
* - Color wheel navigation for color selection
|
||||
* - Mode cycling for lighting patterns
|
||||
* - Confirmation prompts for preset operations
|
||||
*
|
||||
* @note This method is called by the base Menu class when a button press
|
||||
* occurs on a menu item, after the base class has handled standard
|
||||
* navigation operations.
|
||||
* @note Changes are applied immediately to provide instant visual feedback,
|
||||
* but can be reverted if the user cancels or exits without saving.
|
||||
*
|
||||
* @see Menu::onButtonPressed for the base implementation
|
||||
* @see ButtonType for available button types
|
||||
*/
|
||||
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
||||
|
||||
/**
|
||||
* @brief Pointer to menu options configuration structure
|
||||
* @details Stores a reference to the menu configuration passed during construction.
|
||||
* This pointer provides access to the display context and callback functions
|
||||
* needed for menu operations, screen transitions, and lighting control
|
||||
* communication with the hardware subsystem.
|
||||
*
|
||||
* The configuration enables:
|
||||
* - Display context for rendering lighting control interface
|
||||
* - Screen management callbacks for navigation to other menus
|
||||
* - Hardware communication for real-time lighting control
|
||||
* - System callbacks for lighting state persistence
|
||||
*
|
||||
* @note This pointer is not owned by the LightMenu and must remain valid
|
||||
* throughout the menu's lifetime. It is managed by the application framework.
|
||||
*
|
||||
* @warning Must not be modified after construction as it may be shared
|
||||
* with other components and contains critical system callbacks.
|
||||
*/
|
||||
menu_options_t *m_options;
|
||||
};
|
||||
35
firmware/components/insa/include/ui/LightSettingsMenu.h
Normal file
35
firmware/components/insa/include/ui/LightSettingsMenu.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
/**
|
||||
* @class LightSettingsMenu
|
||||
* @brief Menu for configuring light system settings including sections and LED parameters
|
||||
* @details This menu extends the base Menu class to provide specialized functionality
|
||||
* for managing light system configurations. It handles dynamic section management
|
||||
* and provides persistence for user settings.
|
||||
*/
|
||||
class LightSettingsMenu final : public Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a LightSettingsMenu with the specified options
|
||||
* @param options Pointer to menu configuration options structure
|
||||
* @details Initializes the menu with section counter and default section settings
|
||||
*/
|
||||
explicit LightSettingsMenu(menu_options_t *options);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handles button press events for light settings menu items
|
||||
* @param menuItem The menu item that received the button press
|
||||
* @param button The type of button that was pressed
|
||||
* @details Manages value switching, dynamic section list updates, and
|
||||
* persistence of section values when settings are modified
|
||||
*/
|
||||
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
||||
|
||||
static std::string CreateKey(int index);
|
||||
|
||||
menu_options_t *m_options; ///< Pointer to menu configuration options
|
||||
};
|
||||
104
firmware/components/insa/include/ui/MainMenu.h
Normal file
104
firmware/components/insa/include/ui/MainMenu.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @file MainMenu.h
|
||||
* @brief Main menu implementation for the application's primary interface
|
||||
* @details This header defines the MainMenu class which serves as the primary
|
||||
* user interface entry point for the application. It extends the Menu
|
||||
* base class to provide a customized main menu experience with
|
||||
* application-specific menu items and navigation behavior.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
/**
|
||||
* @class MainMenu
|
||||
* @brief Main menu interface class providing the primary application navigation
|
||||
* @details This final class inherits from Menu and represents the main menu interface
|
||||
* of the application. It serves as the primary entry point for user interaction
|
||||
* and provides navigation to all major application features and sub-menus.
|
||||
*
|
||||
* The MainMenu class customizes the base Menu functionality by:
|
||||
* - Defining application-specific menu items during construction
|
||||
* - Implementing custom button handling for main menu navigation
|
||||
* - Managing transitions to sub-menus and application features
|
||||
*
|
||||
* This class is typically the first screen presented to users after the splash
|
||||
* screen and serves as the central hub for all application functionality.
|
||||
*
|
||||
* @note This class is marked as final and cannot be inherited from.
|
||||
*
|
||||
* @see Menu for base menu functionality
|
||||
* @see menu_options_t for configuration structure
|
||||
*/
|
||||
class MainMenu final : public Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs the main menu with the specified configuration
|
||||
* @param options Pointer to menu options configuration structure
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the menu's lifetime
|
||||
* @pre options->u8g2 must be initialized and ready for graphics operations
|
||||
* @pre All callback functions in options must be properly configured
|
||||
* @post MainMenu is initialized with application-specific menu items and ready for use
|
||||
*
|
||||
* @details The constructor initializes the main menu by setting up all the
|
||||
* primary application menu items such as:
|
||||
* - Settings access
|
||||
* - Feature-specific menus
|
||||
* - System controls
|
||||
* - Application exit options
|
||||
*
|
||||
* @note The menu does not take ownership of the options structure and assumes
|
||||
* it remains valid throughout the menu's lifetime.
|
||||
*/
|
||||
explicit MainMenu(menu_options_t *options);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handles button press events specific to main menu items
|
||||
* @param menuItem
|
||||
* @param button Type of button that was pressed
|
||||
*
|
||||
* @details Overrides the base Menu class method to provide main menu-specific
|
||||
* button handling logic. This method processes user interactions with
|
||||
* main menu items and initiates appropriate actions such as:
|
||||
* - Navigation to sub-menus (Settings, Light Control, etc.)
|
||||
* - Direct feature activation
|
||||
* - System operations
|
||||
*
|
||||
* The method uses the menu item ID to determine which action to take and
|
||||
* utilizes the callback functions provided in m_options to perform screen
|
||||
* transitions or other application-level operations.
|
||||
*
|
||||
* @note This method is called by the base Menu class when a button press
|
||||
* occurs on a menu item, after the base class has handled standard
|
||||
* navigation operations.
|
||||
*
|
||||
* @see Menu::onButtonPressed for the base implementation
|
||||
* @see ButtonType for available button types
|
||||
*/
|
||||
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
||||
|
||||
/**
|
||||
* @brief Pointer to menu options configuration structure
|
||||
* @details Stores a reference to the menu configuration passed during construction.
|
||||
* This pointer provides access to the display context and callback functions
|
||||
* needed for menu operations, screen transitions, and user interaction handling.
|
||||
*
|
||||
* The configuration includes:
|
||||
* - Display context for rendering operations
|
||||
* - Screen management callbacks for navigation
|
||||
* - Input handling callbacks for button events
|
||||
*
|
||||
* @note This pointer is not owned by the MainMenu and must remain valid
|
||||
* throughout the menu's lifetime. It is managed by the application framework.
|
||||
*
|
||||
* @warning Must not be modified after construction as it may be shared
|
||||
* with other components.
|
||||
*/
|
||||
menu_options_t *m_options;
|
||||
};
|
||||
142
firmware/components/insa/include/ui/ScreenSaver.h
Normal file
142
firmware/components/insa/include/ui/ScreenSaver.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @file ScreenSaver.h
|
||||
* @brief Animated screensaver implementation with vehicle effect
|
||||
* @details This header defines the ScreenSaver class which provides an animated
|
||||
* vehicle screensaver that activates during periods of user inactivity.
|
||||
* The screensaver displays moving vehicles to prevent screen burn-in while
|
||||
* providing visual feedback that the system is still active and responsive.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MenuOptions.h"
|
||||
#include "common/Widget.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @class ScreenSaver
|
||||
* @brief Animated vehicle screensaver widget for system idle periods
|
||||
*/
|
||||
class ScreenSaver final : public Widget
|
||||
{
|
||||
public:
|
||||
explicit ScreenSaver(menu_options_t *options);
|
||||
void update(uint64_t dt) override;
|
||||
void render() override;
|
||||
void onButtonClicked(ButtonType button) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @enum VehicleType
|
||||
* @brief Types of available vehicles
|
||||
*/
|
||||
enum class VehicleType
|
||||
{
|
||||
CAR,
|
||||
CONVERTABLE,
|
||||
SUV,
|
||||
LORRY,
|
||||
TRUCK
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum Direction
|
||||
* @brief Movement direction for vehicles
|
||||
*/
|
||||
enum class Direction
|
||||
{
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct Vehicle
|
||||
* @brief Individual vehicle object for animation
|
||||
*/
|
||||
struct Vehicle
|
||||
{
|
||||
int x; // X position on screen
|
||||
int y; // Y position on screen
|
||||
float speed; // Movement speed
|
||||
VehicleType type; // Type of vehicle
|
||||
Direction direction; // Movement direction
|
||||
bool active; // Whether a vehicle is currently active
|
||||
};
|
||||
|
||||
static constexpr int MAX_LEFT_VEHICLES = 2;
|
||||
static constexpr int MAX_RIGHT_VEHICLES = 2;
|
||||
static constexpr int MAX_VEHICLES = MAX_LEFT_VEHICLES + MAX_RIGHT_VEHICLES;
|
||||
static constexpr int VEHICLE_SPAWN_DELAY = 2500; // milliseconds
|
||||
static constexpr float MIN_SPEED = 1.0f;
|
||||
static constexpr float MAX_SPEED = 2.0f;
|
||||
static constexpr int MIN_SAME_DIRECTION_DISTANCE = 48; // 32 + 16 pixels
|
||||
static constexpr int MAX_SAME_DIRECTION_DISTANCE = 64; // 32 + 32 pixels
|
||||
|
||||
menu_options_t *m_options;
|
||||
uint64_t m_animationCounter;
|
||||
uint64_t m_lastSpawnTime;
|
||||
|
||||
std::vector<Vehicle> m_vehicles;
|
||||
int m_leftVehicleCount;
|
||||
int m_rightVehicleCount;
|
||||
int m_sceneOffsetX = 0;
|
||||
int m_sceneOffsetY = 0;
|
||||
uint64_t m_sceneShiftTimer = 0;
|
||||
|
||||
/**
|
||||
* @brief Initialize vehicle system
|
||||
*/
|
||||
void initVehicles();
|
||||
|
||||
/**
|
||||
* @brief Spawn a new vehicle if conditions are met
|
||||
*/
|
||||
void trySpawnVehicle();
|
||||
|
||||
/**
|
||||
* @brief Get a random vehicle type
|
||||
* @return Random VehicleType
|
||||
*/
|
||||
static VehicleType getRandomVehicleType();
|
||||
|
||||
/**
|
||||
* @brief Get a random direction with constraint checking
|
||||
* @return Direction for new vehicle
|
||||
*/
|
||||
static Direction getRandomDirection();
|
||||
|
||||
/**
|
||||
* @brief Draw a vehicle at a specified position
|
||||
* @param vehicle Vehicle to draw
|
||||
*/
|
||||
void drawVehicle(const Vehicle &vehicle) const;
|
||||
|
||||
/**
|
||||
* @brief Draw a bitmap with transparency (black pixels are transparent)
|
||||
* @param x X position
|
||||
* @param y Y position
|
||||
* @param width Bitmap width
|
||||
* @param height Bitmap height
|
||||
* @param bitmap Bitmap data
|
||||
*/
|
||||
void drawTransparentBitmap(int x, int y, int width, int height, const unsigned char *bitmap) const;
|
||||
|
||||
/**
|
||||
* @brief Get vehicle bitmap data
|
||||
* @param type Vehicle type
|
||||
* @param direction Movement direction
|
||||
* @param width Output parameter for bitmap width
|
||||
* @param height Output parameter for bitmap height
|
||||
* @return Pointer to bitmap data
|
||||
*/
|
||||
static const unsigned char *getVehicleBitmap(VehicleType type, Direction direction, int &width, int &height);
|
||||
|
||||
/**
|
||||
* @brief Check if there's enough distance to spawn a vehicle in a specific direction
|
||||
* @param direction Direction to check
|
||||
* @return true if spawning is allowed
|
||||
*/
|
||||
bool canSpawnInDirection(Direction direction) const;
|
||||
};
|
||||
77
firmware/components/insa/include/ui/SettingsMenu.h
Normal file
77
firmware/components/insa/include/ui/SettingsMenu.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @file SettingsMenu.h
|
||||
* @brief Settings menu implementation for application configuration
|
||||
* @details This header defines the SettingsMenu class which provides a user interface
|
||||
* for configuring application settings and preferences. It extends the Menu
|
||||
* base class to offer a specialized settings management interface with
|
||||
* various configuration options and system parameters.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
/**
|
||||
* @class SettingsMenu
|
||||
* @brief Settings menu interface class for application configuration management
|
||||
* @details This final class inherits from Menu and provides a comprehensive settings
|
||||
* interface for the application. It allows users to configure various aspects
|
||||
* of the system including display preferences, system behavior, network
|
||||
* settings, and other configurable parameters.
|
||||
*
|
||||
* The SettingsMenu class extends the base Menu functionality by:
|
||||
* - Providing settings-specific menu items (toggles, selections, number inputs)
|
||||
* - Managing configuration persistence and validation
|
||||
* - Organizing settings into logical categories and sections
|
||||
* - Implementing real-time preview of setting changes where applicable
|
||||
*
|
||||
* Typical settings categories include:
|
||||
* - Display settings (brightness, contrast, theme)
|
||||
* - System preferences (timeouts, auto-save, etc.)
|
||||
* - Network configuration (if applicable)
|
||||
* - User interface preferences
|
||||
* - Hardware-specific parameters
|
||||
*
|
||||
* @note This class is marked as final and cannot be inherited from.
|
||||
* @note Settings changes are typically applied immediately or after confirmation,
|
||||
* depending on the specific setting type and system requirements.
|
||||
*
|
||||
* @see Menu for base menu functionality
|
||||
* @see menu_options_t for configuration structure
|
||||
* @see MainMenu for navigation back to main interface
|
||||
*/
|
||||
class SettingsMenu final : public Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs the settings menu with the specified configuration
|
||||
* @param options Pointer to menu options configuration structure
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the menu's lifetime
|
||||
* @pre options->u8g2 must be initialized and ready for graphics operations
|
||||
* @pre All callback functions in options must be properly configured
|
||||
* @post SettingsMenu is initialized with all available configuration options and ready for use
|
||||
*
|
||||
* @details The constructor initializes the settings menu by creating all the
|
||||
* configuration menu items based on the current system state and
|
||||
* available options. This includes:
|
||||
* - Loading current setting values from persistent storage
|
||||
* - Creating appropriate menu items (toggles, selections, number inputs)
|
||||
* - Setting up validation ranges and allowed values
|
||||
* - Organizing items in a logical, user-friendly order
|
||||
*
|
||||
* The menu automatically detects which settings are available based on
|
||||
* hardware capabilities and system configuration, ensuring only relevant
|
||||
* options are presented to the user.
|
||||
*
|
||||
* @note The menu does not take ownership of the options structure and assumes
|
||||
* it remains valid throughout the menu's lifetime.
|
||||
* @note Setting values are loaded from persistent storage during construction
|
||||
* and changes are typically saved automatically or on confirmation.
|
||||
*
|
||||
* @see Menu::Menu for base class construction details
|
||||
*/
|
||||
explicit SettingsMenu(menu_options_t *options);
|
||||
};
|
||||
177
firmware/components/insa/include/ui/SplashScreen.h
Normal file
177
firmware/components/insa/include/ui/SplashScreen.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @file SplashScreen.h
|
||||
* @brief Application splash screen implementation for startup presentation
|
||||
* @details This header defines the SplashScreen class which provides the initial
|
||||
* visual presentation when the application starts. It serves as a loading
|
||||
* screen that displays branding information, initialization progress, and
|
||||
* provides visual feedback during the application startup sequence.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MenuOptions.h"
|
||||
#include "common/Widget.h"
|
||||
|
||||
/**
|
||||
* @class SplashScreen
|
||||
* @brief Application startup screen widget with branding and initialization feedback
|
||||
* @details This final class inherits from Widget and represents the initial screen
|
||||
* displayed when the application starts. It serves multiple purposes including
|
||||
* brand presentation, system initialization feedback, and smooth transition
|
||||
* preparation to the main application interface.
|
||||
*
|
||||
* The SplashScreen class provides:
|
||||
* - Brand identity display (logos, company information, product name)
|
||||
* - System initialization progress indication
|
||||
* - Smooth animations and visual effects for professional appearance
|
||||
* - Automatic transition timing to main menu after initialization
|
||||
* - Error indication if initialization fails
|
||||
*
|
||||
* Key features include:
|
||||
* - Time-based automatic progression to main menu
|
||||
* - Animated elements (fade-in effects, progress indicators)
|
||||
* - Version information display
|
||||
* - Loading status messages
|
||||
* - Graceful handling of initialization delays
|
||||
*
|
||||
* The splash screen typically displays for a minimum duration to ensure users
|
||||
* can read branding information, even if system initialization completes quickly.
|
||||
* It automatically transitions to the main menu once both the minimum display
|
||||
* time and system initialization are complete.
|
||||
*
|
||||
* @note This class is marked as final and cannot be inherited from.
|
||||
* @note The splash screen does not handle user input - it operates on timing
|
||||
* and system state rather than user interaction.
|
||||
*
|
||||
* @see Widget for base widget functionality
|
||||
* @see menu_options_t for configuration structure
|
||||
* @see MainMenu for the target transition screen
|
||||
*/
|
||||
class SplashScreen final : public Widget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs the splash screen with specified configuration
|
||||
* @param options Pointer to menu options configuration structure
|
||||
*
|
||||
* @pre options must not be nullptr and must remain valid for the splash screen's lifetime
|
||||
* @pre options->u8g2 must be initialized and ready for graphics operations
|
||||
* @pre Screen transition callbacks in options must be properly configured
|
||||
* @post SplashScreen is initialized and ready to display startup sequence
|
||||
*
|
||||
* @details The constructor initializes the splash screen by:
|
||||
* - Setting up the initial display state and animations
|
||||
* - Preparing branding elements (logos, text, version info)
|
||||
* - Initializing timing controls for minimum display duration
|
||||
* - Configuring transition parameters for smooth progression
|
||||
* - Loading any required graphics resources or assets
|
||||
*
|
||||
* The splash screen prepares all visual elements during construction to
|
||||
* ensure smooth rendering performance during the critical startup phase.
|
||||
* It also establishes the timing framework for controlling display duration
|
||||
* and transition timing.
|
||||
*
|
||||
* @note The splash screen does not take ownership of the options structure
|
||||
* and assumes it remains valid throughout the screen's lifetime.
|
||||
* @note Graphics resources are loaded during construction, so any required
|
||||
* assets must be available at initialization time.
|
||||
*
|
||||
* @see Widget::Widget for base class construction details
|
||||
*/
|
||||
explicit SplashScreen(menu_options_t *options);
|
||||
|
||||
/**
|
||||
* @brief Updates the splash screen state, animations, and timing logic
|
||||
* @param dt Delta time in milliseconds since the last update call
|
||||
*
|
||||
* @details Overrides the base Widget update method to handle splash screen-specific
|
||||
* logic including:
|
||||
* - Animation progression (fade effects, transitions, progress indicators)
|
||||
* - Timing control for minimum display duration
|
||||
* - System initialization status monitoring
|
||||
* - Automatic transition preparation to main menu
|
||||
* - Loading progress updates and status message changes
|
||||
*
|
||||
* The update method manages the splash screen's lifecycle by tracking
|
||||
* elapsed time and system readiness state. It ensures the splash screen
|
||||
* remains visible for a minimum duration while also monitoring system
|
||||
* initialization completion. Once both conditions are met, it initiates
|
||||
* the transition to the main application interface.
|
||||
*
|
||||
* Animation updates include:
|
||||
* - Fade-in effects for branding elements
|
||||
* - Progress bar or spinner animations
|
||||
* - Text transitions for status messages
|
||||
* - Smooth preparation for screen transition
|
||||
*
|
||||
* @note This method is called every frame during the splash screen display
|
||||
* and must be efficient to maintain smooth visual presentation.
|
||||
* @note The method automatically handles transition to the main menu when
|
||||
* appropriate, using the callback functions provided in m_options.
|
||||
*
|
||||
* @see Widget::update for the base update interface
|
||||
*/
|
||||
void update(uint64_t dt) override;
|
||||
|
||||
/**
|
||||
* @brief Renders the splash screen visual elements to the display
|
||||
*
|
||||
* @details Overrides the base Widget render method to draw all splash screen
|
||||
* elements including branding, status information, and visual effects.
|
||||
* The rendering includes:
|
||||
* - Company/product logos and branding elements
|
||||
* - Application name and version information
|
||||
* - Loading progress indicators (progress bars, spinners, etc.)
|
||||
* - Status messages indicating initialization progress
|
||||
* - Background graphics and visual effects
|
||||
*
|
||||
* The render method creates a professional, polished appearance that
|
||||
* reinforces brand identity while providing useful feedback about the
|
||||
* application startup process. All elements are positioned and styled
|
||||
* to create a cohesive, attractive presentation.
|
||||
*
|
||||
* Rendering features include:
|
||||
* - Centered layout with balanced visual hierarchy
|
||||
* - Smooth animations and transitions
|
||||
* - Consistent typography and color scheme
|
||||
* - Progress feedback elements
|
||||
* - Error indication if initialization problems occur
|
||||
*
|
||||
* @pre u8g2 display context must be initialized and ready for drawing
|
||||
* @post All splash screen visual elements are drawn to the display buffer
|
||||
*
|
||||
* @note This method is called every frame and must be efficient to maintain
|
||||
* smooth visual presentation during the startup sequence.
|
||||
* @note The visual design should be consistent with the overall application
|
||||
* theme and branding guidelines.
|
||||
*
|
||||
* @see Widget::render for the base render interface
|
||||
*/
|
||||
void render() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Pointer to menu options configuration structure
|
||||
* @details Stores a reference to the menu configuration passed during construction.
|
||||
* This provides access to the display context for rendering operations
|
||||
* and screen transition callbacks for automatic progression to the main menu.
|
||||
*
|
||||
* The configuration enables:
|
||||
* - Display context (u8g2) for graphics rendering operations
|
||||
* - Screen transition callbacks for automatic progression to main menu
|
||||
* - System integration for initialization status monitoring
|
||||
*
|
||||
* The splash screen uses the setScreen callback to automatically transition
|
||||
* to the main menu once the startup sequence is complete. This ensures a
|
||||
* seamless user experience from application launch to main interface.
|
||||
*
|
||||
* @note This pointer is not owned by the SplashScreen and must remain valid
|
||||
* throughout the screen's lifetime. It is managed by the application framework.
|
||||
*
|
||||
* @warning Must not be modified after construction as it contains critical
|
||||
* system callbacks required for proper application flow.
|
||||
*/
|
||||
menu_options_t *m_options;
|
||||
};
|
||||
35
firmware/components/insa/src/common/InactivityTracker.cpp
Normal file
35
firmware/components/insa/src/common/InactivityTracker.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "common/InactivityTracker.h"
|
||||
|
||||
InactivityTracker::InactivityTracker(const uint64_t timeoutMs, const std::function<void()> &onTimeout)
|
||||
: m_timeoutMs(timeoutMs), m_elapsedTime(0), m_enabled(true), m_onTimeout(onTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
void InactivityTracker::update(uint64_t dt)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_elapsedTime += dt;
|
||||
|
||||
if (m_elapsedTime >= m_timeoutMs && m_onTimeout)
|
||||
{
|
||||
m_onTimeout();
|
||||
m_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InactivityTracker::reset()
|
||||
{
|
||||
m_elapsedTime = 0;
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
void InactivityTracker::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
386
firmware/components/insa/src/common/Menu.cpp
Normal file
386
firmware/components/insa/src/common/Menu.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
#include "common/Menu.h"
|
||||
|
||||
#include "common/ScrollBar.h"
|
||||
#include "u8g2.h"
|
||||
|
||||
// Menu item type constants for better readability
|
||||
namespace MenuItemTypes
|
||||
{
|
||||
constexpr uint8_t TEXT = 0;
|
||||
constexpr uint8_t SELECTION = 1;
|
||||
constexpr uint8_t TOGGLE = 2;
|
||||
constexpr uint8_t TEXT_COUNTER = 3;
|
||||
} // namespace MenuItemTypes
|
||||
|
||||
// UI layout constants
|
||||
namespace UIConstants
|
||||
{
|
||||
constexpr int LEFT_MARGIN = 8;
|
||||
constexpr int RIGHT_PADDING = 8;
|
||||
constexpr int SCROLLBAR_WIDTH = 3;
|
||||
constexpr int FRAME_BOX_SIZE = 14;
|
||||
constexpr int FRAME_OFFSET = 11;
|
||||
constexpr int SELECTION_MARGIN = 10;
|
||||
constexpr int CORNER_RADIUS = 3;
|
||||
constexpr int LINE_SPACING = 14;
|
||||
constexpr int BOTTOM_OFFSET = 10;
|
||||
} // namespace UIConstants
|
||||
|
||||
Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options)
|
||||
{
|
||||
// Set up button callback using lambda to forward to member function
|
||||
m_options->onButtonClicked = [this](const ButtonType button) { onButtonClicked(button); };
|
||||
}
|
||||
|
||||
Menu::~Menu()
|
||||
{
|
||||
// Clean up callback to prevent dangling pointer
|
||||
if (m_options)
|
||||
{
|
||||
m_options->onButtonClicked = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem Menu::getItem(const int index)
|
||||
{
|
||||
return m_items.at(index);
|
||||
}
|
||||
|
||||
size_t Menu::getItemCount() const
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
void Menu::setItemSize(const size_t size)
|
||||
{
|
||||
if ((m_items.size() - 1) < size)
|
||||
{
|
||||
for (size_t i = m_items.size() - 1; i < size; i++)
|
||||
{
|
||||
auto caption = std::string("Bereich ") + std::to_string(i + 1);
|
||||
auto index = 0;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
constexpr int key_length = 20;
|
||||
char key[key_length] = "";
|
||||
snprintf(key, key_length, "section_%zu", i + 1);
|
||||
index = m_options->persistenceManager->GetValue(key, index);
|
||||
}
|
||||
addSelection(i + 1, caption, m_items.at(0).getValues(), index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.erase(m_items.begin() + static_cast<int>(size + 1), m_items.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggle(const MenuItem &menuItem)
|
||||
{
|
||||
const auto item =
|
||||
menuItem.copyWith(menuItem.getValue() == std::to_string(true) ? std::to_string(false) : std::to_string(true));
|
||||
replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
|
||||
MenuItem Menu::switchValue(const MenuItem &menuItem, ButtonType button)
|
||||
{
|
||||
MenuItem result = menuItem;
|
||||
switch (button)
|
||||
{
|
||||
case ButtonType::LEFT:
|
||||
if (menuItem.getIndex() > 0)
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getIndex() - 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getItemCount() - 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonType::RIGHT:
|
||||
if (menuItem.getIndex() < menuItem.getItemCount() - 1)
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getIndex() + 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto item = menuItem.copyWith(0);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MenuItem Menu::replaceItem(const int index, const MenuItem &item)
|
||||
{
|
||||
m_items.at(index) = item;
|
||||
return item;
|
||||
}
|
||||
|
||||
void Menu::render()
|
||||
{
|
||||
// Initialize selection if not set
|
||||
if (m_selected_item >= m_items.size() && !m_items.empty())
|
||||
{
|
||||
m_selected_item = 0;
|
||||
}
|
||||
|
||||
// Early return if no items to render
|
||||
if (m_items.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear screen with black background
|
||||
u8g2_SetDrawColor(u8g2, 0);
|
||||
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
|
||||
|
||||
// Set white foreground color for drawing
|
||||
u8g2_SetDrawColor(u8g2, 1);
|
||||
|
||||
// Draw UI components
|
||||
drawScrollBar();
|
||||
drawSelectionBox();
|
||||
|
||||
// Calculate center position for main item
|
||||
const int centerY = u8g2->height / 2 + 3;
|
||||
|
||||
// Render the currently selected item (main/center item)
|
||||
const auto &selectedItem = m_items[m_selected_item];
|
||||
renderWidget(&selectedItem, u8g2_font_helvB08_tr, UIConstants::LEFT_MARGIN, centerY);
|
||||
|
||||
// Render previous item (above) if available
|
||||
if (m_selected_item > 0)
|
||||
{
|
||||
const auto &prevItem = m_items[m_selected_item - 1];
|
||||
renderWidget(&prevItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN, UIConstants::LINE_SPACING);
|
||||
}
|
||||
|
||||
// Render next item (below) if available
|
||||
if (m_selected_item < m_items.size() - 1)
|
||||
{
|
||||
const auto &nextItem = m_items[m_selected_item + 1];
|
||||
renderWidget(&nextItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN,
|
||||
u8g2->height - UIConstants::BOTTOM_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const
|
||||
{
|
||||
// Set font and draw main text
|
||||
u8g2_SetFont(u8g2, font);
|
||||
u8g2_DrawStr(u8g2, x, y, item->getText().c_str());
|
||||
|
||||
// Render type-specific elements
|
||||
switch (item->getType())
|
||||
{
|
||||
case MenuItemTypes::TEXT: {
|
||||
const std::string formattedValue = ">";
|
||||
const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str());
|
||||
u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItemTypes::TEXT_COUNTER: {
|
||||
const std::string formattedValue = "(" + item->getValue() + ") >";
|
||||
const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str());
|
||||
u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItemTypes::SELECTION: {
|
||||
// Format selection value with angle brackets
|
||||
const std::string formattedValue = "< " + item->getValue() + " >";
|
||||
const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str());
|
||||
u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItemTypes::TOGGLE: {
|
||||
// Draw checkbox frame
|
||||
const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN;
|
||||
const int frameY = y - UIConstants::FRAME_OFFSET;
|
||||
u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE);
|
||||
|
||||
// Draw checkmark (X) if toggle is true
|
||||
if (item->getValue() == std::to_string(true))
|
||||
{
|
||||
const int checkX1 = frameX + 2;
|
||||
const int checkY1 = frameY + 2;
|
||||
const int checkX2 = frameX + UIConstants::FRAME_BOX_SIZE - 3;
|
||||
const int checkY2 = frameY + UIConstants::FRAME_BOX_SIZE - 3;
|
||||
|
||||
// Draw X pattern for checked state
|
||||
u8g2_DrawLine(u8g2, checkX1, checkY1, checkX2, checkY2);
|
||||
u8g2_DrawLine(u8g2, checkX1, checkY2, checkX2, checkY1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// No additional rendering needed for text and number types
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::onButtonClicked(const ButtonType button)
|
||||
{
|
||||
// Map button input to navigation functions
|
||||
switch (button)
|
||||
{
|
||||
case ButtonType::UP:
|
||||
onPressedUp();
|
||||
break;
|
||||
|
||||
case ButtonType::DOWN:
|
||||
onPressedDown();
|
||||
break;
|
||||
|
||||
case ButtonType::LEFT:
|
||||
onPressedLeft();
|
||||
break;
|
||||
|
||||
case ButtonType::RIGHT:
|
||||
onPressedRight();
|
||||
break;
|
||||
|
||||
case ButtonType::SELECT:
|
||||
onPressedSelect();
|
||||
break;
|
||||
|
||||
case ButtonType::BACK:
|
||||
onPressedBack();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore unknown button inputs
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::onPressedDown()
|
||||
{
|
||||
if (m_items.empty())
|
||||
return;
|
||||
|
||||
// Wrap around to first item when at the end
|
||||
m_selected_item = (m_selected_item + 1) % m_items.size();
|
||||
}
|
||||
|
||||
void Menu::onPressedUp()
|
||||
{
|
||||
if (m_items.empty())
|
||||
return;
|
||||
|
||||
// Wrap around to last item when at the beginning
|
||||
m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1;
|
||||
}
|
||||
|
||||
void Menu::onPressedLeft() const
|
||||
{
|
||||
if (m_selected_item < m_items.size())
|
||||
{
|
||||
const auto &item = m_items.at(m_selected_item);
|
||||
item.onButtonPressed(ButtonType::LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::onPressedRight() const
|
||||
{
|
||||
if (m_selected_item < m_items.size())
|
||||
{
|
||||
const auto &item = m_items.at(m_selected_item);
|
||||
item.onButtonPressed(ButtonType::RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::onPressedSelect() const
|
||||
{
|
||||
if (m_selected_item < m_items.size())
|
||||
{
|
||||
const auto &item = m_items.at(m_selected_item);
|
||||
item.onButtonPressed(ButtonType::SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::onPressedBack() const
|
||||
{
|
||||
// Navigate back to previous screen if callback is available
|
||||
if (m_options && m_options->popScreen)
|
||||
{
|
||||
m_options->popScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::addText(uint8_t id, const std::string &text)
|
||||
{
|
||||
addTextCounter(id, text, 0);
|
||||
}
|
||||
|
||||
void Menu::addTextCounter(uint8_t id, const std::string &text, const uint8_t value)
|
||||
{
|
||||
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
|
||||
onButtonPressed(menuItem, button);
|
||||
};
|
||||
if (value > 0)
|
||||
{
|
||||
m_items.emplace_back(id, MenuItemTypes::TEXT_COUNTER, text, std::to_string(value), callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::addSelection(uint8_t id, const std::string &text, const std::vector<std::string> &values, const int index)
|
||||
{
|
||||
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
|
||||
onButtonPressed(menuItem, button);
|
||||
};
|
||||
m_items.emplace_back(id, MenuItemTypes::SELECTION, text, values, index, callback);
|
||||
}
|
||||
|
||||
void Menu::addToggle(uint8_t id, const std::string &text, const bool selected)
|
||||
{
|
||||
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
|
||||
onButtonPressed(menuItem, button);
|
||||
};
|
||||
m_items.emplace_back(id, MenuItemTypes::TOGGLE, text, std::to_string(selected), callback);
|
||||
}
|
||||
|
||||
void Menu::drawScrollBar() const
|
||||
{
|
||||
// Create scrollbar instance
|
||||
ScrollBar scrollBar(m_options, u8g2->width - UIConstants::SCROLLBAR_WIDTH, 3, 1, u8g2->height - 6);
|
||||
scrollBar.refresh(m_selected_item, m_items.size());
|
||||
scrollBar.render();
|
||||
}
|
||||
|
||||
void Menu::drawSelectionBox() const
|
||||
{
|
||||
// Calculate dimensions for the selection box
|
||||
const auto displayHeight = u8g2->height;
|
||||
const auto displayWidth = u8g2->width;
|
||||
const auto boxHeight = displayHeight / 3;
|
||||
const auto y = boxHeight * 2 - 2;
|
||||
const auto x = displayWidth - UIConstants::RIGHT_PADDING;
|
||||
|
||||
// Draw the rounded frame for the selection box
|
||||
u8g2_DrawRFrame(u8g2, 2, boxHeight, displayWidth - UIConstants::RIGHT_PADDING, boxHeight,
|
||||
UIConstants::CORNER_RADIUS);
|
||||
|
||||
// Draw horizontal line separator
|
||||
u8g2_DrawLine(u8g2, 4, y, displayWidth - UIConstants::RIGHT_PADDING, y);
|
||||
|
||||
// Draw vertical line on the right side
|
||||
u8g2_DrawLine(u8g2, x, y - boxHeight + 3, x, y - 1);
|
||||
}
|
||||
41
firmware/components/insa/src/common/ScrollBar.cpp
Normal file
41
firmware/components/insa/src/common/ScrollBar.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "common/ScrollBar.h"
|
||||
|
||||
ScrollBar::ScrollBar(const menu_options_t *options, const size_t x, const size_t y, const size_t width,
|
||||
const size_t height)
|
||||
: Widget(options->u8g2), m_x(x), m_y(y), m_width(width), m_height(height), m_value(0), m_max(0), m_min(0),
|
||||
m_thumbHeight(0), m_thumbY(0)
|
||||
{
|
||||
}
|
||||
|
||||
void ScrollBar::render()
|
||||
{
|
||||
if (m_max <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t y = m_y; y < m_y + m_height; y += 2)
|
||||
{
|
||||
u8g2_DrawPixel(u8g2, m_x, y);
|
||||
}
|
||||
|
||||
u8g2_DrawBox(u8g2, u8g2->width - 4, m_thumbY, 3, m_thumbHeight);
|
||||
}
|
||||
|
||||
void ScrollBar::refresh(const size_t value, const size_t max, const size_t min)
|
||||
{
|
||||
m_value = value;
|
||||
m_max = max;
|
||||
m_min = min;
|
||||
|
||||
if (m_max <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_thumbHeight = std::max(m_height / 4, static_cast<size_t>(3));
|
||||
|
||||
const size_t trackLength = m_height - m_thumbHeight;
|
||||
|
||||
m_thumbY = m_y + (m_value * trackLength) / (m_max - 1);
|
||||
}
|
||||
33
firmware/components/insa/src/common/Widget.cpp
Normal file
33
firmware/components/insa/src/common/Widget.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "common/Widget.h"
|
||||
|
||||
Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2)
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::enter()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::pause()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::resume()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::exit()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::update(uint64_t dt)
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::render()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::onButtonClicked(ButtonType button)
|
||||
{
|
||||
}
|
||||
99
firmware/components/insa/src/data/MenuItem.cpp
Normal file
99
firmware/components/insa/src/data/MenuItem.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "data/MenuItem.h"
|
||||
|
||||
// Constructor for basic menu items (text buttons)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for menu items with a single value (toggles)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::string value, ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_value(std::move(value)), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for menu items with multiple values (selections)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::vector<std::string> values, int index,
|
||||
ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_values(std::move(values)), m_index(index),
|
||||
m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t MenuItem::getId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
uint8_t MenuItem::getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const std::string &MenuItem::getText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
const std::string &MenuItem::getValue() const
|
||||
{
|
||||
// Return the selected value from values array if available and index is valid
|
||||
if (!m_values.empty() && m_index >= 0 && m_index < m_values.size())
|
||||
{
|
||||
return m_values.at(m_index);
|
||||
}
|
||||
// Otherwise return the direct value
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void MenuItem::setValue(const std::string &value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
void MenuItem::onButtonPressed(const ButtonType button) const
|
||||
{
|
||||
// Execute the callback function if one is registered
|
||||
if (m_callback)
|
||||
{
|
||||
m_callback(*this, button);
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuItem::hasCallback() const
|
||||
{
|
||||
return (m_callback != nullptr);
|
||||
}
|
||||
|
||||
int MenuItem::getIndex() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
std::vector<std::string> MenuItem::getValues() const
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
size_t MenuItem::getItemCount() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
MenuItem MenuItem::copyWith(const std::string &value) const
|
||||
{
|
||||
// Create a copy of this menu item with a new value
|
||||
MenuItem copy = *this;
|
||||
copy.m_value = value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
MenuItem MenuItem::copyWith(const size_t index) const
|
||||
{
|
||||
// Create a copy of this menu item with a new selected index
|
||||
MenuItem copy = *this;
|
||||
|
||||
// Check for potential overflow when converting size_t to int
|
||||
copy.m_index = static_cast<int>(index);
|
||||
return copy;
|
||||
}
|
||||
114
firmware/components/insa/src/ui/LightMenu.cpp
Normal file
114
firmware/components/insa/src/ui/LightMenu.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "ui/LightMenu.h"
|
||||
|
||||
#include "led_manager.h"
|
||||
#include "ui/LightSettingsMenu.h"
|
||||
|
||||
/**
|
||||
* @namespace LightMenuItem
|
||||
* @brief Constants for light menu item identifiers
|
||||
*/
|
||||
namespace LightMenuItem
|
||||
{
|
||||
constexpr uint8_t ACTIVATE = 0; ///< ID for the light activation toggle
|
||||
constexpr uint8_t MODE = 1; ///< ID for the light mode selection
|
||||
constexpr uint8_t LED_SETTINGS = 2; ///< ID for the LED settings menu item
|
||||
} // namespace LightMenuItem
|
||||
|
||||
namespace LightMenuOptions
|
||||
{
|
||||
constexpr std::string LIGHT_ACTIVE = "light_active";
|
||||
constexpr std::string LIGHT_MODE = "light_mode";
|
||||
} // namespace LightMenuOptions
|
||||
|
||||
LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
// Add toggle for enabling/disabling the light system
|
||||
bool active = false;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
active = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_ACTIVE, active);
|
||||
}
|
||||
addToggle(LightMenuItem::ACTIVATE, "Einschalten", active);
|
||||
|
||||
// Create mode selection options (Day/Night modes)
|
||||
std::vector<std::string> values;
|
||||
values.emplace_back("Tag"); // Day mode
|
||||
values.emplace_back("Nacht"); // Night mode
|
||||
int mode_value = 0;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
mode_value = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_MODE, mode_value);
|
||||
}
|
||||
addSelection(LightMenuItem::MODE, "Modus", values, mode_value);
|
||||
|
||||
// Add menu item for accessing LED settings submenu
|
||||
addText(LightMenuItem::LED_SETTINGS, "Einstellungen");
|
||||
}
|
||||
|
||||
void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
std::shared_ptr<Widget> widget;
|
||||
|
||||
// Handle different menu items based on their ID
|
||||
switch (menuItem.getId())
|
||||
{
|
||||
case LightMenuItem::ACTIVATE: {
|
||||
// Toggle the light activation state when SELECT is pressed
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
toggle(menuItem);
|
||||
if (getItem(menuItem.getId()).getValue() == "1")
|
||||
{
|
||||
led_event_data_t payload = {.value = 42};
|
||||
send_event(EVENT_LED_ON, &payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
led_event_data_t payload = {.value = 0};
|
||||
send_event(EVENT_LED_OFF, &payload);
|
||||
}
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(menuItem.getId()).getValue() == "1";
|
||||
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_ACTIVE, value);
|
||||
m_options->persistenceManager->Save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LightMenuItem::MODE: {
|
||||
// Switch between day/night modes using left/right buttons
|
||||
const auto item = switchValue(menuItem, button);
|
||||
if (button == ButtonType::LEFT || button == ButtonType::RIGHT)
|
||||
{
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(item.getId()).getIndex();
|
||||
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_MODE, value);
|
||||
m_options->persistenceManager->Save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LightMenuItem::LED_SETTINGS: {
|
||||
// Open the LED settings submenu when SELECT is pressed
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
widget = std::make_shared<LightSettingsMenu>(m_options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Handle unknown menu items (no action required)
|
||||
break;
|
||||
}
|
||||
|
||||
// Push the new widget to the screen stack if one was created
|
||||
if (m_options && m_options->pushScreen)
|
||||
{
|
||||
m_options->pushScreen(widget);
|
||||
}
|
||||
}
|
||||
57
firmware/components/insa/src/ui/LightSettingsMenu.cpp
Normal file
57
firmware/components/insa/src/ui/LightSettingsMenu.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "ui/LightSettingsMenu.h"
|
||||
|
||||
/**
|
||||
* @namespace LightSettingsMenuItem
|
||||
* @brief Constants for light settings menu item identifiers
|
||||
*/
|
||||
namespace LightSettingsMenuItem
|
||||
{
|
||||
constexpr uint8_t SECTION_COUNTER = 0; ///< ID for the section counter menu item
|
||||
}
|
||||
|
||||
std::string LightSettingsMenu::CreateKey(const int index)
|
||||
{
|
||||
constexpr int key_length = 20;
|
||||
char key[key_length] = "";
|
||||
snprintf(key, key_length, "section_%d", index);
|
||||
return key;
|
||||
}
|
||||
|
||||
LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
// Create values vector for section counts (1-99)
|
||||
std::vector<std::string> values;
|
||||
for (size_t i = 1; i <= 99; i++)
|
||||
{
|
||||
values.emplace_back(std::to_string(i));
|
||||
}
|
||||
|
||||
// Add section counter selection (allows choosing number of sections)
|
||||
auto value = 7;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
value = m_options->persistenceManager->GetValue(CreateKey(0), value);
|
||||
}
|
||||
addSelection(LightSettingsMenuItem::SECTION_COUNTER, "Sektionen", values, value);
|
||||
|
||||
setItemSize(std::stoull(getItem(0).getValue()));
|
||||
}
|
||||
|
||||
void LightSettingsMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
// Handle value switching for the current menu item
|
||||
switchValue(menuItem, button);
|
||||
|
||||
// Update the section list size based on the section counter value
|
||||
if (menuItem.getId() == 0)
|
||||
{
|
||||
setItemSize(std::stoull(getItem(0).getValue()));
|
||||
}
|
||||
|
||||
// Persist the changed section values if persistence is available
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(menuItem.getId()).getIndex();
|
||||
m_options->persistenceManager->SetValue(CreateKey(menuItem.getId()), value);
|
||||
}
|
||||
}
|
||||
44
firmware/components/insa/src/ui/MainMenu.cpp
Normal file
44
firmware/components/insa/src/ui/MainMenu.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "ui/MainMenu.h"
|
||||
|
||||
#include "common/Widget.h"
|
||||
#include "ui/LightMenu.h"
|
||||
#include "ui/SettingsMenu.h"
|
||||
|
||||
namespace MainMenuItem
|
||||
{
|
||||
constexpr uint8_t LIGHT = 0;
|
||||
constexpr uint8_t EXTERNAL_DEVICES = 1;
|
||||
constexpr uint8_t SETTINGS = 2;
|
||||
} // namespace MainMenuItem
|
||||
|
||||
MainMenu::MainMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
addText(MainMenuItem::LIGHT, "Lichtsteuerung");
|
||||
addTextCounter(MainMenuItem::EXTERNAL_DEVICES, "ext. Geraete", 0);
|
||||
addText(MainMenuItem::SETTINGS, "Einstellungen");
|
||||
}
|
||||
|
||||
void MainMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
std::shared_ptr<Widget> widget;
|
||||
switch (menuItem.getId())
|
||||
{
|
||||
case MainMenuItem::LIGHT:
|
||||
widget = std::make_shared<LightMenu>(m_options);
|
||||
break;
|
||||
|
||||
case MainMenuItem::SETTINGS:
|
||||
widget = std::make_shared<SettingsMenu>(m_options);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_options && m_options->pushScreen)
|
||||
{
|
||||
m_options->pushScreen(widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
329
firmware/components/insa/src/ui/ScreenSaver.cpp
Normal file
329
firmware/components/insa/src/ui/ScreenSaver.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "data/roads.h"
|
||||
#include "data/vehicles.h"
|
||||
#include <cstdlib>
|
||||
|
||||
ScreenSaver::ScreenSaver(menu_options_t *options)
|
||||
: Widget(options->u8g2), m_options(options), m_animationCounter(0), m_lastSpawnTime(0), m_leftVehicleCount(0),
|
||||
m_rightVehicleCount(0)
|
||||
{
|
||||
initVehicles();
|
||||
}
|
||||
|
||||
void ScreenSaver::initVehicles()
|
||||
{
|
||||
m_vehicles.resize(MAX_VEHICLES);
|
||||
|
||||
for (auto &vehicle : m_vehicles)
|
||||
{
|
||||
vehicle.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::update(const uint64_t dt)
|
||||
{
|
||||
m_animationCounter += dt;
|
||||
m_lastSpawnTime += dt;
|
||||
m_sceneShiftTimer += dt;
|
||||
|
||||
// Shift entire scene every 30 seconds
|
||||
if (m_sceneShiftTimer > 30000)
|
||||
{
|
||||
m_sceneOffsetX = (random() % 7) - 3; // -3 to +3 pixels
|
||||
m_sceneOffsetY = (random() % 7) - 3; // -3 to +3 pixels
|
||||
m_sceneShiftTimer = 0;
|
||||
}
|
||||
|
||||
// Try to spawn a new vehicle every few seconds
|
||||
if (m_lastSpawnTime > VEHICLE_SPAWN_DELAY)
|
||||
{
|
||||
trySpawnVehicle();
|
||||
m_lastSpawnTime = 0;
|
||||
}
|
||||
|
||||
// Update vehicle positions
|
||||
if (m_animationCounter > 16) // ~60 FPS
|
||||
{
|
||||
m_animationCounter = 0;
|
||||
|
||||
for (auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (!vehicle.active)
|
||||
continue;
|
||||
|
||||
// Move vehicle
|
||||
if (vehicle.direction == Direction::LEFT)
|
||||
{
|
||||
vehicle.x -= static_cast<int>(vehicle.speed);
|
||||
|
||||
// Remove the vehicle if it goes off-screen
|
||||
if (vehicle.x <= -32) // Allow for largest vehicle width
|
||||
{
|
||||
vehicle.active = false;
|
||||
m_leftVehicleCount--;
|
||||
}
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
vehicle.x += static_cast<int>(vehicle.speed);
|
||||
|
||||
// Remove the vehicle if it goes off-screen
|
||||
if (vehicle.x >= (u8g2->width + 32)) // Allow for largest vehicle width
|
||||
{
|
||||
vehicle.active = false;
|
||||
m_rightVehicleCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenSaver::canSpawnInDirection(Direction direction) const
|
||||
{
|
||||
// Minimalen Abstand zwischen 48 und 64 Pixel zufällig wählen
|
||||
int requiredDistance =
|
||||
MIN_SAME_DIRECTION_DISTANCE + (random() % (MAX_SAME_DIRECTION_DISTANCE - MIN_SAME_DIRECTION_DISTANCE + 1));
|
||||
|
||||
for (const auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (!vehicle.active || vehicle.direction != direction)
|
||||
continue;
|
||||
|
||||
// Abstand zum nächsten Fahrzeug in gleicher Richtung prüfen
|
||||
if (direction == Direction::LEFT)
|
||||
{
|
||||
// Fahrzeuge fahren von rechts nach links
|
||||
// Neues Fahrzeug würde bei u8g2->width + 16 starten
|
||||
int newVehicleX = u8g2->width + 16;
|
||||
|
||||
// Prüfen ob genug Abstand zum existierenden Fahrzeug
|
||||
if (newVehicleX - vehicle.x < requiredDistance)
|
||||
return false;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Fahrzeuge fahren von links nach rechts
|
||||
// Neues Fahrzeug würde bei -32 starten
|
||||
int newVehicleX = -32;
|
||||
|
||||
// Prüfen ob genug Abstand zum existierenden Fahrzeug
|
||||
if (vehicle.x - newVehicleX < requiredDistance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScreenSaver::trySpawnVehicle()
|
||||
{
|
||||
// Check if we can spawn a new vehicle
|
||||
int activeVehicles = 0;
|
||||
int availableSlot = -1;
|
||||
|
||||
for (int i = 0; i < MAX_VEHICLES; i++)
|
||||
{
|
||||
if (m_vehicles[i].active)
|
||||
{
|
||||
activeVehicles++;
|
||||
}
|
||||
else if (availableSlot == -1)
|
||||
{
|
||||
availableSlot = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't spawn if we're at max capacity or no slot available
|
||||
if (activeVehicles >= MAX_VEHICLES || availableSlot == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Direction direction = getRandomDirection();
|
||||
|
||||
// Check direction constraints
|
||||
if ((direction == Direction::LEFT && m_leftVehicleCount >= MAX_LEFT_VEHICLES) ||
|
||||
(direction == Direction::RIGHT && m_rightVehicleCount >= MAX_RIGHT_VEHICLES))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canSpawnInDirection(direction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new vehicle
|
||||
Vehicle &newVehicle = m_vehicles[availableSlot];
|
||||
newVehicle.type = getRandomVehicleType();
|
||||
newVehicle.direction = direction;
|
||||
newVehicle.speed = MIN_SPEED + (static_cast<float>(random()) / RAND_MAX) * (MAX_SPEED - MIN_SPEED);
|
||||
|
||||
// Set Y position based on a direction (simulate opposing traffic lanes)
|
||||
const int halfHeight = u8g2->height / 2;
|
||||
if (direction == Direction::RIGHT)
|
||||
{
|
||||
// Vehicles going LEFT use bottom half of screen
|
||||
newVehicle.y = halfHeight + 8 + (random() % (halfHeight - 24));
|
||||
m_rightVehicleCount++;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Vehicles going RIGHT use top half of screen
|
||||
newVehicle.y = 8 + (random() % (halfHeight - 24));
|
||||
m_leftVehicleCount++;
|
||||
}
|
||||
|
||||
// Set the starting X position based on the direction
|
||||
if (direction == Direction::LEFT)
|
||||
{
|
||||
// Vehicles going LEFT (from right to left) start from RIGHT side of screen
|
||||
newVehicle.x = u8g2->width + 16;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Vehicles going RIGHT (from left to right) start from LEFT side of screen
|
||||
newVehicle.x = -32; // Account for the largest vehicle width
|
||||
}
|
||||
|
||||
newVehicle.active = true;
|
||||
}
|
||||
|
||||
ScreenSaver::VehicleType ScreenSaver::getRandomVehicleType()
|
||||
{
|
||||
switch (random() % 5)
|
||||
{
|
||||
case 0:
|
||||
return VehicleType::CAR;
|
||||
case 1:
|
||||
return VehicleType::CONVERTABLE;
|
||||
case 2:
|
||||
return VehicleType::SUV;
|
||||
case 3:
|
||||
return VehicleType::LORRY;
|
||||
case 4:
|
||||
return VehicleType::TRUCK;
|
||||
default:
|
||||
return VehicleType::CAR;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSaver::Direction ScreenSaver::getRandomDirection()
|
||||
{
|
||||
// Simple 50/50 chance for each direction
|
||||
return (random() % 2 == 0) ? Direction::LEFT : Direction::RIGHT;
|
||||
}
|
||||
|
||||
void ScreenSaver::render()
|
||||
{
|
||||
// Clear screen with a black background
|
||||
u8g2_SetDrawColor(u8g2, 0);
|
||||
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
|
||||
u8g2_SetDrawColor(u8g2, 1);
|
||||
|
||||
// Calculate offsets
|
||||
const int roadOffset = (m_animationCounter / 100) % road_horizontal_width;
|
||||
|
||||
// Draw all active vehicles with a scene offset
|
||||
for (const auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (vehicle.active)
|
||||
{
|
||||
Vehicle offsetVehicle = vehicle;
|
||||
offsetVehicle.x += m_sceneOffsetX;
|
||||
offsetVehicle.y += m_sceneOffsetY;
|
||||
drawVehicle(offsetVehicle);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw road with offsets
|
||||
const int y = u8g2->height / 2 - road_horizontal_height / 2 + m_sceneOffsetY;
|
||||
for (int x = -road_horizontal_width + roadOffset + m_sceneOffsetX; x <= u8g2->width; x += road_horizontal_width)
|
||||
{
|
||||
drawTransparentBitmap(x, y, road_horizontal_width, road_horizontal_height, road_horizontal_bits);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::drawVehicle(const Vehicle &vehicle) const
|
||||
{
|
||||
int width, height;
|
||||
|
||||
if (const unsigned char *bitmap = getVehicleBitmap(vehicle.type, vehicle.direction, width, height))
|
||||
{
|
||||
drawTransparentBitmap(vehicle.x, vehicle.y, width, height, bitmap);
|
||||
// u8g2_DrawXBM(u8g2, vehicle.x, vehicle.y, width, height, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::drawTransparentBitmap(const int x, const int y, const int width, const int height,
|
||||
const unsigned char *bitmap) const
|
||||
{
|
||||
for (int py = 0; py < height; py++)
|
||||
{
|
||||
for (int px = 0; px < width; px++)
|
||||
{
|
||||
// Calculate byte and a bit of position in bitmap
|
||||
const int byteIndex = (py * ((width + 7) / 8)) + (px / 8);
|
||||
|
||||
// Check if the pixel is set (white)
|
||||
if (const int bitIndex = px % 8; bitmap[byteIndex] & (1 << bitIndex))
|
||||
{
|
||||
// Only draw white pixels, skip black (transparent) pixels
|
||||
const int screenX = x + px;
|
||||
|
||||
// Bounds checking
|
||||
if (const int screenY = y + py;
|
||||
screenX >= 0 && screenX < u8g2->width && screenY >= 0 && screenY < u8g2->height)
|
||||
{
|
||||
u8g2_DrawPixel(u8g2, screenX, screenY);
|
||||
}
|
||||
}
|
||||
// Black pixels are simply not drawn (transparent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char *ScreenSaver::getVehicleBitmap(const VehicleType type, const Direction direction, int &width,
|
||||
int &height)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VehicleType::CAR:
|
||||
width = car_width;
|
||||
height = car_height;
|
||||
return (direction == Direction::LEFT) ? car_left_bits : car_right_bits;
|
||||
|
||||
case VehicleType::CONVERTABLE:
|
||||
width = convertable_width;
|
||||
height = convertable_height;
|
||||
return (direction == Direction::LEFT) ? convertable_left_bits : convertable_right_bits;
|
||||
|
||||
case VehicleType::SUV:
|
||||
width = suv_width;
|
||||
height = suv_height;
|
||||
return (direction == Direction::LEFT) ? suv_left_bits : suv_right_bits;
|
||||
|
||||
case VehicleType::LORRY:
|
||||
width = lorry_width;
|
||||
height = lorry_height;
|
||||
return (direction == Direction::LEFT) ? lorry_left_bits : lorry_right_bits;
|
||||
|
||||
case VehicleType::TRUCK:
|
||||
width = truck_width;
|
||||
height = truck_height;
|
||||
return (direction == Direction::LEFT) ? truck_left_bits : truck_right_bits;
|
||||
|
||||
default:
|
||||
width = car_width;
|
||||
height = car_height;
|
||||
return car_left_bits;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::onButtonClicked(ButtonType button)
|
||||
{
|
||||
if (m_options && m_options->popScreen)
|
||||
{
|
||||
m_options->popScreen();
|
||||
}
|
||||
}
|
||||
11
firmware/components/insa/src/ui/SettingsMenu.cpp
Normal file
11
firmware/components/insa/src/ui/SettingsMenu.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "ui/SettingsMenu.h"
|
||||
|
||||
namespace SettingsMenuItem
|
||||
{
|
||||
constexpr uint8_t OTA_UPLOAD = 0;
|
||||
}
|
||||
|
||||
SettingsMenu::SettingsMenu(menu_options_t *options) : Menu(options)
|
||||
{
|
||||
addText(SettingsMenuItem::OTA_UPLOAD, "OTA Einspielen");
|
||||
}
|
||||
39
firmware/components/insa/src/ui/SplashScreen.cpp
Normal file
39
firmware/components/insa/src/ui/SplashScreen.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "ui/SplashScreen.h"
|
||||
|
||||
#include "ui/MainMenu.h"
|
||||
|
||||
#ifndef ESP32
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
uint64_t counter = 0;
|
||||
|
||||
SplashScreen::SplashScreen(menu_options_t *options) : Widget(options->u8g2), m_options(options)
|
||||
{
|
||||
}
|
||||
|
||||
void SplashScreen::update(const uint64_t dt)
|
||||
{
|
||||
counter += dt;
|
||||
if (counter >= 3000)
|
||||
{
|
||||
counter = 0;
|
||||
if (m_options && m_options->setScreen)
|
||||
{
|
||||
m_options->setScreen(std::make_shared<MainMenu>(m_options));
|
||||
}
|
||||
}
|
||||
#ifndef ESP32
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SplashScreen::render()
|
||||
{
|
||||
u8g2_SetFont(u8g2, u8g2_font_DigitalDisco_tr);
|
||||
u8g2_DrawStr(u8g2, 28, u8g2->height / 2 - 10, "HO Anlage");
|
||||
u8g2_DrawStr(u8g2, 30, u8g2->height / 2 + 5, "Axel Janz");
|
||||
u8g2_SetFont(u8g2, u8g2_font_haxrcorp4089_tr);
|
||||
u8g2_DrawStr(u8g2, 35, 50, "Initialisierung...");
|
||||
}
|
||||
28
firmware/components/led-manager/CMakeLists.txt
Normal file
28
firmware/components/led-manager/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
idf_component_register(SRCS
|
||||
src/hal_esp32/led_manager.cpp
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
u8g2
|
||||
esp_event
|
||||
persistence-manager
|
||||
)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(led-manager)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
src/hal_native/led_manager.cpp
|
||||
src/hal_native/Matrix.cpp
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
SDL3::SDL3
|
||||
persistence-manager
|
||||
)
|
||||
2
firmware/components/led-manager/idf_component.yml
Executable file
2
firmware/components/led-manager/idf_component.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
espressif/led_strip: '~3.0.1'
|
||||
29
firmware/components/led-manager/include/Matrix.h
Normal file
29
firmware/components/led-manager/include/Matrix.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "SDL3/SDL_render.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Matrix
|
||||
{
|
||||
public:
|
||||
explicit Matrix(SDL_WindowID windowId, SDL_Renderer *renderer, uint8_t cols, uint8_t rows);
|
||||
|
||||
[[nodiscard]] SDL_Renderer *renderer() const;
|
||||
|
||||
void Render() const;
|
||||
|
||||
[[nodiscard]] SDL_WindowID windowId() const;
|
||||
|
||||
private:
|
||||
void DrawColoredGrid() const;
|
||||
|
||||
SDL_WindowID m_windowId;
|
||||
SDL_Renderer *m_renderer;
|
||||
|
||||
uint8_t m_cols;
|
||||
uint8_t m_rows;
|
||||
|
||||
static constexpr float cellSize = 50.0f;
|
||||
static constexpr float spacing = 1.0f;
|
||||
};
|
||||
22
firmware/components/led-manager/include/led_manager.h
Normal file
22
firmware/components/led-manager/include/led_manager.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum
|
||||
{
|
||||
EVENT_LED_ON,
|
||||
EVENT_LED_OFF,
|
||||
EVENT_LED_DAY,
|
||||
EVENT_LED_NIGHT,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int value;
|
||||
} led_event_data_t;
|
||||
|
||||
uint64_t wled_init();
|
||||
|
||||
uint64_t register_handler();
|
||||
|
||||
uint64_t send_event(uint32_t event, led_event_data_t *payload);
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "led_manager.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "led_strip.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
led_strip_handle_t led_strip;
|
||||
|
||||
static const uint32_t value = 5;
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(LED_EVENTS_BASE);
|
||||
ESP_EVENT_DEFINE_BASE(LED_EVENTS_BASE);
|
||||
|
||||
esp_event_loop_handle_t loop_handle;
|
||||
|
||||
const char *TAG = "LED";
|
||||
|
||||
uint64_t wled_init(void)
|
||||
{
|
||||
led_strip_config_t strip_config = {.strip_gpio_num = CONFIG_WLED_DIN_PIN,
|
||||
.max_leds = 64,
|
||||
.led_model = LED_MODEL_WS2812,
|
||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_RGB,
|
||||
.flags = {
|
||||
.invert_out = false,
|
||||
}};
|
||||
|
||||
led_strip_rmt_config_t rmt_config = {.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 0,
|
||||
.mem_block_symbols = 0,
|
||||
.flags = {
|
||||
.with_dma = true,
|
||||
}};
|
||||
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
|
||||
for (uint32_t i = 0; i < 64; i++)
|
||||
{
|
||||
led_strip_set_pixel(led_strip, i, 0, 0, 0);
|
||||
}
|
||||
led_strip_refresh(led_strip);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data)
|
||||
{
|
||||
if (id == EVENT_LED_ON || id == EVENT_LED_OFF)
|
||||
{
|
||||
auto brightness = (id == EVENT_LED_ON) ? value : 0;
|
||||
for (uint32_t i = 0; i < 64; i++)
|
||||
{
|
||||
led_strip_set_pixel(led_strip, i, brightness, brightness, brightness);
|
||||
}
|
||||
led_strip_refresh(led_strip);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t register_handler(void)
|
||||
{
|
||||
esp_event_loop_args_t loop_args = {
|
||||
.queue_size = 2, .task_name = "led_manager", .task_priority = 5, .task_stack_size = 4096, .task_core_id = 1};
|
||||
esp_event_loop_create(&loop_args, &loop_handle);
|
||||
|
||||
esp_event_handler_register_with(loop_handle, LED_EVENTS_BASE, ESP_EVENT_ANY_ID, event_handler, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint64_t send_event(uint32_t event, led_event_data_t *payload)
|
||||
{
|
||||
if (payload == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_event_post_to(loop_handle, // Event-Loop Handle
|
||||
LED_EVENTS_BASE, // Event Base
|
||||
event, // Event ID (EVENT_LED_ON, EVENT_LED_OFF, etc.)
|
||||
payload, // Daten-Pointer
|
||||
sizeof(led_event_data_t), // Datengröße
|
||||
portMAX_DELAY // Wartezeit
|
||||
);
|
||||
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE("LED", "Failed to post event: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
64
firmware/components/led-manager/src/hal_native/Matrix.cpp
Normal file
64
firmware/components/led-manager/src/hal_native/Matrix.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "Matrix.h"
|
||||
|
||||
#include "SDL3/SDL.h"
|
||||
|
||||
Matrix::Matrix(uint32_t windowID, SDL_Renderer *renderer, const uint8_t cols, const uint8_t rows)
|
||||
: m_windowId(windowID), m_renderer(renderer), m_cols(cols), m_rows(rows)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_Renderer *Matrix::renderer() const
|
||||
{
|
||||
return m_renderer;
|
||||
}
|
||||
|
||||
SDL_WindowID Matrix::windowId() const
|
||||
{
|
||||
return m_windowId;
|
||||
}
|
||||
|
||||
void Matrix::DrawColoredGrid() const
|
||||
{
|
||||
int i = 0;
|
||||
for (int w = 0; w < m_cols; w++)
|
||||
{
|
||||
const auto phase = w % (2 * m_rows);
|
||||
|
||||
for (int h_raw = 0; h_raw < m_rows; h_raw++)
|
||||
{
|
||||
int h;
|
||||
if (phase < m_rows)
|
||||
{
|
||||
h = h_raw;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = m_rows - 1 - h_raw;
|
||||
}
|
||||
|
||||
constexpr auto rectSize = cellSize - 2.0f * spacing;
|
||||
|
||||
const auto x = static_cast<float>(w) * cellSize + spacing;
|
||||
const auto y = static_cast<float>(h) * cellSize + spacing;
|
||||
|
||||
auto rect = SDL_FRect{x, y, rectSize, rectSize};
|
||||
|
||||
i++;
|
||||
const auto red = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
const auto green = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
const auto blue = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
SDL_SetRenderDrawColor(m_renderer, red, green, blue, 255);
|
||||
SDL_RenderFillRect(m_renderer, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::Render() const
|
||||
{
|
||||
SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(m_renderer);
|
||||
|
||||
DrawColoredGrid();
|
||||
|
||||
SDL_RenderPresent(m_renderer);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "led_manager.h"
|
||||
|
||||
uint64_t wled_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t register_handler(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t send_event(uint32_t event, led_event_data_t *payload)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
24
firmware/components/persistence-manager/CMakeLists.txt
Normal file
24
firmware/components/persistence-manager/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
idf_component_register(SRCS
|
||||
src/hal_esp32/PersistenceManager.cpp
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES
|
||||
nvs_flash
|
||||
)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(persistence-manager)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
src/hal_native/PersistenceManager.cpp
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
SDL3::SDL3
|
||||
)
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @interface IPersistenceManager
|
||||
* @brief Abstract interface for platform-independent persistence management
|
||||
* @details This interface defines the contract for key-value storage and retrieval
|
||||
* systems across different platforms (Desktop/SDL3 and ESP32).
|
||||
*/
|
||||
class IPersistenceManager
|
||||
{
|
||||
public:
|
||||
virtual ~IPersistenceManager() = default;
|
||||
|
||||
/**
|
||||
* @brief Template methods for type-safe setting and retrieving of values
|
||||
* @tparam T The type of value to set (must be one of: bool, int, float, double, std::string)
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to store
|
||||
*/
|
||||
template<typename T>
|
||||
void SetValue(const std::string& key, const T& value) {
|
||||
static_assert(std::is_same_v<T, bool> ||
|
||||
std::is_same_v<T, int> ||
|
||||
std::is_same_v<T, float> ||
|
||||
std::is_same_v<T, double> ||
|
||||
std::is_same_v<T, std::string>,
|
||||
"Unsupported type for IPersistenceManager");
|
||||
SetValueImpl(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template method for type-safe retrieval of values
|
||||
* @tparam T The type of value to retrieve
|
||||
* @param key The key to look up
|
||||
* @param defaultValue The default value to return if the key is not found
|
||||
* @return The stored value or default value if the key doesn't exist
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]] T GetValue(const std::string& key, const T& defaultValue = T{}) const {
|
||||
return GetValueImpl<T>(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility methods for key management
|
||||
*/
|
||||
[[nodiscard]] virtual bool HasKey(const std::string& key) const = 0; ///< Check if a key exists
|
||||
virtual void RemoveKey(const std::string& key) = 0; ///< Remove a key-value pair
|
||||
virtual void Clear() = 0; ///< Clear all stored data
|
||||
[[nodiscard]] virtual size_t GetKeyCount() const = 0; ///< Get the number of stored keys
|
||||
|
||||
/**
|
||||
* @brief Persistence operations
|
||||
*/
|
||||
virtual bool Save() = 0; ///< Save data to persistent storage
|
||||
virtual bool Load() = 0; ///< Load data from persistent storage
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Template-specific implementations that must be overridden by derived classes
|
||||
* @details These methods handle the actual storage and retrieval of different data types
|
||||
*/
|
||||
virtual void SetValueImpl(const std::string& key, bool value) = 0;
|
||||
virtual void SetValueImpl(const std::string& key, int value) = 0;
|
||||
virtual void SetValueImpl(const std::string& key, float value) = 0;
|
||||
virtual void SetValueImpl(const std::string& key, double value) = 0;
|
||||
virtual void SetValueImpl(const std::string& key, const std::string& value) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool GetValueImpl(const std::string& key, bool defaultValue) const = 0;
|
||||
[[nodiscard]] virtual int GetValueImpl(const std::string& key, int defaultValue) const = 0;
|
||||
[[nodiscard]] virtual float GetValueImpl(const std::string& key, float defaultValue) const = 0;
|
||||
[[nodiscard]] virtual double GetValueImpl(const std::string& key, double defaultValue) const = 0;
|
||||
[[nodiscard]] virtual std::string GetValueImpl(const std::string& key, const std::string& defaultValue) const = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Template dispatch methods for type-safe value retrieval
|
||||
* @tparam T The type to retrieve
|
||||
* @param key The key to look up
|
||||
* @param defaultValue The default value to return
|
||||
* @return The retrieved value or default if not found
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]] T GetValueImpl(const std::string& key, const T& defaultValue) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
return GetValueImpl(key, static_cast<bool>(defaultValue));
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
return GetValueImpl(key, static_cast<int>(defaultValue));
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
return GetValueImpl(key, static_cast<float>(defaultValue));
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
return GetValueImpl(key, static_cast<double>(defaultValue));
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
return GetValueImpl(key, static_cast<const std::string&>(defaultValue));
|
||||
} else {
|
||||
static_assert(std::is_same_v<T, bool> ||
|
||||
std::is_same_v<T, int> ||
|
||||
std::is_same_v<T, float> ||
|
||||
std::is_same_v<T, double> ||
|
||||
std::is_same_v<T, std::string>,
|
||||
"Unsupported type for IPersistenceManager");
|
||||
return defaultValue; // This line will never be reached, but satisfies the compiler
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "IPersistenceManager.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
/**
|
||||
* @class PersistenceManager
|
||||
* @brief ESP32-specific implementation using NVS (Non-Volatile Storage)
|
||||
* @details This implementation uses ESP32's NVS API for persistent storage
|
||||
* in flash memory, providing a platform-optimized solution for
|
||||
* embedded systems.
|
||||
*/
|
||||
class PersistenceManager final : public IPersistenceManager
|
||||
{
|
||||
private:
|
||||
nvs_handle_t nvs_handle_;
|
||||
std::string namespace_;
|
||||
bool initialized_;
|
||||
|
||||
public:
|
||||
explicit PersistenceManager(const std::string &nvs_namespace = "config");
|
||||
~PersistenceManager() override;
|
||||
|
||||
bool HasKey(const std::string &key) const override;
|
||||
void RemoveKey(const std::string &key) override;
|
||||
void Clear() override;
|
||||
size_t GetKeyCount() const override;
|
||||
|
||||
bool Save() override;
|
||||
bool Load() override;
|
||||
|
||||
bool Initialize();
|
||||
void Deinitialize();
|
||||
bool IsInitialized() const
|
||||
{
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetValueImpl(const std::string &key, bool value) override;
|
||||
void SetValueImpl(const std::string &key, int value) override;
|
||||
void SetValueImpl(const std::string &key, float value) override;
|
||||
void SetValueImpl(const std::string &key, double value) override;
|
||||
void SetValueImpl(const std::string &key, const std::string &value) override;
|
||||
|
||||
bool GetValueImpl(const std::string &key, bool defaultValue) const override;
|
||||
int GetValueImpl(const std::string &key, int defaultValue) const override;
|
||||
float GetValueImpl(const std::string &key, float defaultValue) const override;
|
||||
double GetValueImpl(const std::string &key, double defaultValue) const override;
|
||||
std::string GetValueImpl(const std::string &key, const std::string &defaultValue) const override;
|
||||
|
||||
private:
|
||||
bool EnsureInitialized() const;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "../IPersistenceManager.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
class PersistenceManager final : public IPersistenceManager {
|
||||
public:
|
||||
using ValueType = std::variant<
|
||||
bool,
|
||||
int,
|
||||
float,
|
||||
double,
|
||||
std::string
|
||||
>;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, ValueType> m_data;
|
||||
std::string m_filename;
|
||||
|
||||
public:
|
||||
explicit PersistenceManager(std::string filename = "settings.dat");
|
||||
~PersistenceManager() override;
|
||||
|
||||
[[nodiscard]] bool HasKey(const std::string& key) const override;
|
||||
void RemoveKey(const std::string& key) override;
|
||||
void Clear() override;
|
||||
[[nodiscard]] size_t GetKeyCount() const override { return m_data.size(); }
|
||||
|
||||
bool Save() override;
|
||||
bool Load() override;
|
||||
|
||||
bool SaveToFile(const std::string& filename);
|
||||
bool LoadFromFile(const std::string& filename);
|
||||
|
||||
protected:
|
||||
void SetValueImpl(const std::string& key, bool value) override;
|
||||
void SetValueImpl(const std::string& key, int value) override;
|
||||
void SetValueImpl(const std::string& key, float value) override;
|
||||
void SetValueImpl(const std::string& key, double value) override;
|
||||
void SetValueImpl(const std::string& key, const std::string& value) override;
|
||||
|
||||
[[nodiscard]] bool GetValueImpl(const std::string& key, bool defaultValue) const override;
|
||||
[[nodiscard]] int GetValueImpl(const std::string& key, int defaultValue) const override;
|
||||
[[nodiscard]] float GetValueImpl(const std::string& key, float defaultValue) const override;
|
||||
[[nodiscard]] double GetValueImpl(const std::string& key, double defaultValue) const override;
|
||||
[[nodiscard]] std::string GetValueImpl(const std::string& key, const std::string& defaultValue) const override;
|
||||
|
||||
private:
|
||||
static bool WriteValueToStream(SDL_IOStream* stream, const ValueType& value) ;
|
||||
static bool ReadValueFromStream(SDL_IOStream* stream, ValueType& value) ;
|
||||
|
||||
enum class TypeId : uint8_t {
|
||||
BOOL = 0,
|
||||
INT = 1,
|
||||
FLOAT = 2,
|
||||
DOUBLE = 3,
|
||||
STRING = 4
|
||||
};
|
||||
|
||||
static TypeId GetTypeId(const ValueType& value);
|
||||
};
|
||||
@@ -0,0 +1,293 @@
|
||||
#include "hal_esp32/PersistenceManager.h"
|
||||
#include <cstring>
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "PersistenceManager";
|
||||
|
||||
PersistenceManager::PersistenceManager(const std::string &nvs_namespace)
|
||||
: namespace_(nvs_namespace), initialized_(false)
|
||||
{
|
||||
Initialize();
|
||||
Load();
|
||||
}
|
||||
|
||||
PersistenceManager::~PersistenceManager()
|
||||
{
|
||||
Deinitialize();
|
||||
}
|
||||
|
||||
bool PersistenceManager::Initialize()
|
||||
{
|
||||
if (initialized_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize NVS
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
err = nvs_flash_init();
|
||||
}
|
||||
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to initialize NVS flash: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open NVS handle
|
||||
err = nvs_open(namespace_.c_str(), NVS_READWRITE, &nvs_handle_);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "PersistenceManager initialized with namespace: %s", namespace_.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void PersistenceManager::Deinitialize()
|
||||
{
|
||||
if (initialized_)
|
||||
{
|
||||
nvs_close(nvs_handle_);
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PersistenceManager::EnsureInitialized() const
|
||||
{
|
||||
if (!initialized_)
|
||||
{
|
||||
ESP_LOGE(TAG, "PersistenceManager not initialized");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PersistenceManager::HasKey(const std::string &key) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return false;
|
||||
|
||||
size_t required_size = 0;
|
||||
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), nullptr, &required_size);
|
||||
return err == ESP_OK;
|
||||
}
|
||||
|
||||
void PersistenceManager::RemoveKey(const std::string &key)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_erase_key(nvs_handle_, key.c_str());
|
||||
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to remove key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void PersistenceManager::Clear()
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_erase_all(nvs_handle_);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to clear all keys: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
size_t PersistenceManager::GetKeyCount() const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return 0;
|
||||
|
||||
nvs_iterator_t it = nullptr;
|
||||
esp_err_t err = nvs_entry_find(NVS_DEFAULT_PART_NAME, namespace_.c_str(), NVS_TYPE_ANY, &it);
|
||||
|
||||
if (err != ESP_OK || it == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
while (it != nullptr)
|
||||
{
|
||||
count++;
|
||||
err = nvs_entry_next(&it);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nvs_release_iterator(it);
|
||||
return count;
|
||||
}
|
||||
|
||||
bool PersistenceManager::Save()
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return false;
|
||||
|
||||
esp_err_t err = nvs_commit(nvs_handle_);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to commit NVS: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PersistenceManager::Load()
|
||||
{
|
||||
return EnsureInitialized();
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, bool value)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
uint8_t val = value ? 1 : 0;
|
||||
esp_err_t err = nvs_set_u8(nvs_handle_, key.c_str(), val);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to set bool key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, int value)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_set_i32(nvs_handle_, key.c_str(), value);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to set int key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, float value)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_set_blob(nvs_handle_, key.c_str(), &value, sizeof(float));
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to set float key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, double value)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_set_blob(nvs_handle_, key.c_str(), &value, sizeof(double));
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to set double key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, const std::string &value)
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return;
|
||||
|
||||
esp_err_t err = nvs_set_str(nvs_handle_, key.c_str(), value.c_str());
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to set string key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
bool PersistenceManager::GetValueImpl(const std::string &key, bool defaultValue) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return defaultValue;
|
||||
|
||||
uint8_t value;
|
||||
esp_err_t err = nvs_get_u8(nvs_handle_, key.c_str(), &value);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
int PersistenceManager::GetValueImpl(const std::string &key, int defaultValue) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return defaultValue;
|
||||
|
||||
int32_t value;
|
||||
esp_err_t err = nvs_get_i32(nvs_handle_, key.c_str(), &value);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
float PersistenceManager::GetValueImpl(const std::string &key, float defaultValue) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return defaultValue;
|
||||
|
||||
float value;
|
||||
size_t required_size = sizeof(float);
|
||||
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), &value, &required_size);
|
||||
if (err != ESP_OK || required_size != sizeof(float))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
double PersistenceManager::GetValueImpl(const std::string &key, double defaultValue) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return defaultValue;
|
||||
|
||||
double value;
|
||||
size_t required_size = sizeof(double);
|
||||
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), &value, &required_size);
|
||||
if (err != ESP_OK || required_size != sizeof(double))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string PersistenceManager::GetValueImpl(const std::string &key, const std::string &defaultValue) const
|
||||
{
|
||||
if (!EnsureInitialized())
|
||||
return defaultValue;
|
||||
|
||||
size_t required_size = 0;
|
||||
esp_err_t err = nvs_get_str(nvs_handle_, key.c_str(), nullptr, &required_size);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string value(required_size - 1, '\0'); // -1 for null terminator
|
||||
err = nvs_get_str(nvs_handle_, key.c_str(), value.data(), &required_size);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
#include "hal_native/PersistenceManager.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
PersistenceManager::PersistenceManager(std::string filename) : m_filename(std::move(filename))
|
||||
{
|
||||
if (!SDL_WasInit(SDL_INIT_EVENTS))
|
||||
{
|
||||
SDL_Init(SDL_INIT_EVENTS);
|
||||
}
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
PersistenceManager::~PersistenceManager()
|
||||
{
|
||||
Save();
|
||||
}
|
||||
|
||||
bool PersistenceManager::HasKey(const std::string &key) const
|
||||
{
|
||||
return m_data.contains(key);
|
||||
}
|
||||
|
||||
void PersistenceManager::RemoveKey(const std::string &key)
|
||||
{
|
||||
m_data.erase(key);
|
||||
}
|
||||
|
||||
void PersistenceManager::Clear()
|
||||
{
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
bool PersistenceManager::Save()
|
||||
{
|
||||
return SaveToFile(m_filename);
|
||||
}
|
||||
|
||||
bool PersistenceManager::Load()
|
||||
{
|
||||
return LoadFromFile(m_filename);
|
||||
}
|
||||
|
||||
bool PersistenceManager::SaveToFile(const std::string &filename)
|
||||
{
|
||||
SDL_IOStream *stream = SDL_IOFromFile(filename.c_str(), "wb");
|
||||
if (!stream)
|
||||
{
|
||||
SDL_Log("Error opening file for writing: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t count = m_data.size();
|
||||
if (SDL_WriteIO(stream, &count, sizeof(count)) != sizeof(count))
|
||||
{
|
||||
SDL_Log("Error writing count: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &[key, value] : m_data)
|
||||
{
|
||||
size_t keyLength = key.length();
|
||||
if (SDL_WriteIO(stream, &keyLength, sizeof(keyLength)) != sizeof(keyLength))
|
||||
{
|
||||
SDL_Log("Error writing key length: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_WriteIO(stream, key.c_str(), keyLength) != keyLength)
|
||||
{
|
||||
SDL_Log("Error writing key: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WriteValueToStream(stream, value))
|
||||
{
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_CloseIO(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PersistenceManager::LoadFromFile(const std::string &filename)
|
||||
{
|
||||
SDL_IOStream *stream = SDL_IOFromFile(filename.c_str(), "rb");
|
||||
if (!stream)
|
||||
{
|
||||
SDL_Log("File not found or error opening: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data.clear();
|
||||
|
||||
size_t count;
|
||||
if (SDL_ReadIO(stream, &count, sizeof(count)) != sizeof(count))
|
||||
{
|
||||
SDL_Log("Error reading count: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
size_t keyLength;
|
||||
if (SDL_ReadIO(stream, &keyLength, sizeof(keyLength)) != sizeof(keyLength))
|
||||
{
|
||||
SDL_Log("Error reading key length: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string key(keyLength, '\0');
|
||||
if (SDL_ReadIO(stream, key.data(), keyLength) != keyLength)
|
||||
{
|
||||
SDL_Log("Error reading key: %s", SDL_GetError());
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueType value;
|
||||
if (!ReadValueFromStream(stream, value))
|
||||
{
|
||||
SDL_CloseIO(stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
SDL_CloseIO(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, bool value)
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, int value)
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, float value)
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, double value)
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
void PersistenceManager::SetValueImpl(const std::string &key, const std::string &value)
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
|
||||
bool PersistenceManager::GetValueImpl(const std::string &key, bool defaultValue) const
|
||||
{
|
||||
if (const auto it = m_data.find(key); it != m_data.end() && std::holds_alternative<bool>(it->second))
|
||||
{
|
||||
return std::get<bool>(it->second);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int PersistenceManager::GetValueImpl(const std::string &key, int defaultValue) const
|
||||
{
|
||||
if (const auto it = m_data.find(key); it != m_data.end() && std::holds_alternative<int>(it->second))
|
||||
{
|
||||
return std::get<int>(it->second);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
float PersistenceManager::GetValueImpl(const std::string &key, float defaultValue) const
|
||||
{
|
||||
if (const auto it = m_data.find(key); it != m_data.end() && std::holds_alternative<float>(it->second))
|
||||
{
|
||||
return std::get<float>(it->second);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
double PersistenceManager::GetValueImpl(const std::string &key, double defaultValue) const
|
||||
{
|
||||
if (const auto it = m_data.find(key); it != m_data.end() && std::holds_alternative<double>(it->second))
|
||||
{
|
||||
return std::get<double>(it->second);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
std::string PersistenceManager::GetValueImpl(const std::string &key, const std::string &defaultValue) const
|
||||
{
|
||||
if (const auto it = m_data.find(key); it != m_data.end() && std::holds_alternative<std::string>(it->second))
|
||||
{
|
||||
return std::get<std::string>(it->second);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool PersistenceManager::WriteValueToStream(SDL_IOStream *stream, const ValueType &value)
|
||||
{
|
||||
const TypeId typeId = GetTypeId(value);
|
||||
|
||||
if (SDL_WriteIO(stream, &typeId, sizeof(typeId)) != sizeof(typeId))
|
||||
{
|
||||
SDL_Log("Error writing type ID: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case TypeId::BOOL: {
|
||||
const bool val = std::get<bool>(value);
|
||||
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||
}
|
||||
case TypeId::INT: {
|
||||
const int val = std::get<int>(value);
|
||||
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||
}
|
||||
case TypeId::FLOAT: {
|
||||
const float val = std::get<float>(value);
|
||||
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||
}
|
||||
case TypeId::DOUBLE: {
|
||||
const double val = std::get<double>(value);
|
||||
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||
}
|
||||
case TypeId::STRING: {
|
||||
const auto &str = std::get<std::string>(value);
|
||||
const size_t length = str.length();
|
||||
|
||||
if (SDL_WriteIO(stream, &length, sizeof(length)) != sizeof(length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SDL_WriteIO(stream, str.c_str(), length) == length;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PersistenceManager::ReadValueFromStream(SDL_IOStream *stream, ValueType &value)
|
||||
{
|
||||
TypeId typeId;
|
||||
|
||||
if (SDL_ReadIO(stream, &typeId, sizeof(typeId)) != sizeof(typeId))
|
||||
{
|
||||
SDL_Log("Error reading type ID: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case TypeId::BOOL: {
|
||||
bool val;
|
||||
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val))
|
||||
{
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeId::INT: {
|
||||
int val;
|
||||
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val))
|
||||
{
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeId::FLOAT: {
|
||||
float val;
|
||||
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val))
|
||||
{
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeId::DOUBLE: {
|
||||
double val;
|
||||
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val))
|
||||
{
|
||||
value = val;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeId::STRING: {
|
||||
size_t length;
|
||||
if (SDL_ReadIO(stream, &length, sizeof(length)) != sizeof(length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string str(length, '\0');
|
||||
if (SDL_ReadIO(stream, str.data(), length) == length)
|
||||
{
|
||||
value = str;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Log("Error reading value: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
PersistenceManager::TypeId PersistenceManager::GetTypeId(const ValueType &value)
|
||||
{
|
||||
if (std::holds_alternative<bool>(value))
|
||||
return TypeId::BOOL;
|
||||
if (std::holds_alternative<int>(value))
|
||||
return TypeId::INT;
|
||||
if (std::holds_alternative<float>(value))
|
||||
return TypeId::FLOAT;
|
||||
if (std::holds_alternative<double>(value))
|
||||
return TypeId::DOUBLE;
|
||||
if (std::holds_alternative<std::string>(value))
|
||||
return TypeId::STRING;
|
||||
|
||||
return TypeId::BOOL;
|
||||
}
|
||||
BIN
firmware/config.dat
Normal file
BIN
firmware/config.dat
Normal file
Binary file not shown.
16
firmware/main/CMakeLists.txt
Executable file
16
firmware/main/CMakeLists.txt
Executable file
@@ -0,0 +1,16 @@
|
||||
idf_component_register(SRCS
|
||||
main.cpp
|
||||
app_task.cpp
|
||||
../components/persistence-manager/src/hal_esp32/PersistenceManager.cpp
|
||||
button_handling.c
|
||||
hal/u8g2_esp32_hal.c
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES
|
||||
insa
|
||||
led-manager
|
||||
persistence-manager
|
||||
u8g2
|
||||
driver
|
||||
esp_timer
|
||||
esp_event
|
||||
)
|
||||
7
firmware/main/Kconfig.projbuild
Normal file
7
firmware/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,7 @@
|
||||
menu "Warnemuende Lighthouse"
|
||||
config WLED_DIN_PIN
|
||||
int "WLED Data In Pin"
|
||||
default 14
|
||||
help
|
||||
The number of the WLED data in pin.
|
||||
endmenu
|
||||
185
firmware/main/app_task.cpp
Normal file
185
firmware/main/app_task.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "app_task.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "hal/u8g2_esp32_hal.h"
|
||||
#include "u8g2.h"
|
||||
|
||||
#include "button_handling.h"
|
||||
#include "common/InactivityTracker.h"
|
||||
#include "hal_esp32/PersistenceManager.h"
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "ui/SplashScreen.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#define PIN_SDA GPIO_NUM_35
|
||||
#define PIN_SCL GPIO_NUM_36
|
||||
#else
|
||||
/// just dummy pins, because of compile check
|
||||
#define PIN_SDA GPIO_NUM_20
|
||||
#define PIN_SCL GPIO_NUM_21
|
||||
#endif
|
||||
#define PIN_RST GPIO_NUM_NC
|
||||
|
||||
static const char *TAG = "app_task";
|
||||
|
||||
u8g2_t u8g2;
|
||||
uint8_t last_value = 0;
|
||||
menu_options_t options;
|
||||
uint8_t received_signal;
|
||||
|
||||
std::shared_ptr<Widget> m_widget;
|
||||
std::vector<std::shared_ptr<Widget>> m_history;
|
||||
std::unique_ptr<InactivityTracker> m_inactivityTracker;
|
||||
|
||||
extern QueueHandle_t buttonQueue;
|
||||
|
||||
static void setup_screen(void)
|
||||
{
|
||||
u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
|
||||
u8g2_esp32_hal.bus.i2c.sda = PIN_SDA;
|
||||
u8g2_esp32_hal.bus.i2c.scl = PIN_SCL;
|
||||
u8g2_esp32_hal.reset = PIN_RST;
|
||||
u8g2_esp32_hal_init(u8g2_esp32_hal);
|
||||
|
||||
u8g2_Setup_sh1106_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);
|
||||
u8x8_SetI2CAddress(&u8g2.u8x8, 0x3C * 2);
|
||||
|
||||
ESP_LOGI(TAG, "u8g2_InitDisplay");
|
||||
u8g2_InitDisplay(&u8g2);
|
||||
|
||||
ESP_LOGI(TAG, "u8g2_SetPowerSave");
|
||||
u8g2_SetPowerSave(&u8g2, 0);
|
||||
}
|
||||
|
||||
void setScreen(const std::shared_ptr<Widget> &screen)
|
||||
{
|
||||
if (screen != nullptr)
|
||||
{
|
||||
m_widget = screen;
|
||||
m_history.clear();
|
||||
m_history.emplace_back(m_widget);
|
||||
m_widget->enter();
|
||||
}
|
||||
}
|
||||
|
||||
void pushScreen(const std::shared_ptr<Widget> &screen)
|
||||
{
|
||||
if (screen != nullptr)
|
||||
{
|
||||
if (m_widget)
|
||||
{
|
||||
m_widget->pause();
|
||||
}
|
||||
m_widget = screen;
|
||||
m_widget->enter();
|
||||
m_history.emplace_back(m_widget);
|
||||
}
|
||||
}
|
||||
|
||||
void popScreen()
|
||||
{
|
||||
if (m_history.size() >= 2)
|
||||
{
|
||||
m_history.pop_back();
|
||||
if (m_widget)
|
||||
{
|
||||
m_widget->exit();
|
||||
}
|
||||
m_widget = m_history.back();
|
||||
m_widget->resume();
|
||||
}
|
||||
}
|
||||
|
||||
static void init_ui(void)
|
||||
{
|
||||
options = {
|
||||
.u8g2 = &u8g2,
|
||||
.setScreen = [](const std::shared_ptr<Widget> &screen) { setScreen(screen); },
|
||||
.pushScreen = [](const std::shared_ptr<Widget> &screen) { pushScreen(screen); },
|
||||
.popScreen = []() { popScreen(); },
|
||||
.onButtonClicked = nullptr,
|
||||
.persistenceManager = std::make_shared<PersistenceManager>(),
|
||||
};
|
||||
m_widget = std::make_shared<SplashScreen>(&options);
|
||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||
auto screensaver = std::make_shared<ScreenSaver>(&options);
|
||||
options.pushScreen(screensaver);
|
||||
});
|
||||
}
|
||||
|
||||
static void handle_button(uint8_t button)
|
||||
{
|
||||
m_inactivityTracker->reset();
|
||||
|
||||
if (m_widget)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case 1:
|
||||
m_widget->onButtonClicked(ButtonType::UP);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
m_widget->onButtonClicked(ButtonType::LEFT);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
m_widget->onButtonClicked(ButtonType::RIGHT);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
m_widget->onButtonClicked(ButtonType::DOWN);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
m_widget->onButtonClicked(ButtonType::BACK);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
m_widget->onButtonClicked(ButtonType::SELECT);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGI(TAG, "Unhandled button: %u", button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_task(void *args)
|
||||
{
|
||||
setup_screen();
|
||||
setup_buttons();
|
||||
init_ui();
|
||||
|
||||
auto oldTime = esp_timer_get_time();
|
||||
|
||||
while (true)
|
||||
{
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
|
||||
if (m_widget != nullptr)
|
||||
{
|
||||
auto currentTime = esp_timer_get_time();
|
||||
auto delta = currentTime - oldTime;
|
||||
oldTime = currentTime;
|
||||
|
||||
uint64_t deltaMs = delta / 1000;
|
||||
|
||||
m_widget->update(deltaMs);
|
||||
m_widget->render();
|
||||
|
||||
m_inactivityTracker->update(deltaMs);
|
||||
}
|
||||
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
|
||||
if (xQueueReceive(buttonQueue, &received_signal, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
handle_button(received_signal);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_buttons();
|
||||
}
|
||||
3
firmware/main/app_task.h
Normal file
3
firmware/main/app_task.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void app_task(void *args);
|
||||
88
firmware/main/button_handling.c
Normal file
88
firmware/main/button_handling.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "button_handling.h"
|
||||
|
||||
#include "button_gpio.h"
|
||||
#include "common.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "button_handling";
|
||||
|
||||
const uint8_t gpios[] = {BUTTON_DOWN, BUTTON_UP, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_SELECT, BUTTON_BACK};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t gpio;
|
||||
} button_user_data_t;
|
||||
|
||||
static button_user_data_t button_data[6];
|
||||
|
||||
QueueHandle_t buttonQueue = NULL;
|
||||
|
||||
static void button_event_cb(void *arg, void *usr_data)
|
||||
{
|
||||
if (buttonQueue == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Button queue not initialized!");
|
||||
return;
|
||||
}
|
||||
button_user_data_t *data = (button_user_data_t *)usr_data;
|
||||
uint8_t gpio_num = data->gpio;
|
||||
|
||||
ESP_LOGI(TAG, "Button pressed on GPIO %d", gpio_num);
|
||||
|
||||
if (xQueueSend(buttonQueue, &gpio_num, 0) != pdTRUE)
|
||||
{
|
||||
ESP_LOGW(TAG, "Failed to send button press to queue");
|
||||
}
|
||||
}
|
||||
|
||||
static void create_button(uint8_t gpio, int index)
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = gpio,
|
||||
.active_level = 0,
|
||||
.enable_power_save = true,
|
||||
};
|
||||
button_handle_t gpio_btn = NULL;
|
||||
const esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &gpio_btn);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Button create failed");
|
||||
}
|
||||
|
||||
button_data[index].gpio = gpio;
|
||||
iot_button_register_cb(gpio_btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, &button_data[index]);
|
||||
}
|
||||
|
||||
void setup_buttons(void)
|
||||
{
|
||||
buttonQueue = xQueueCreate(10, sizeof(uint8_t));
|
||||
if (buttonQueue == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to create button queue");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Button queue created successfully");
|
||||
for (int i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++)
|
||||
{
|
||||
create_button(gpios[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup function (optional)
|
||||
void cleanup_buttons(void)
|
||||
{
|
||||
if (buttonQueue != NULL)
|
||||
{
|
||||
vQueueDelete(buttonQueue);
|
||||
}
|
||||
}
|
||||
11
firmware/main/button_handling.h
Normal file
11
firmware/main/button_handling.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
void setup_buttons(void);
|
||||
void cleanup_buttons(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
8
firmware/main/common.h
Normal file
8
firmware/main/common.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define BUTTON_UP GPIO_NUM_1
|
||||
#define BUTTON_DOWN GPIO_NUM_6
|
||||
#define BUTTON_LEFT GPIO_NUM_3
|
||||
#define BUTTON_RIGHT GPIO_NUM_5
|
||||
#define BUTTON_SELECT GPIO_NUM_18
|
||||
#define BUTTON_BACK GPIO_NUM_16
|
||||
262
firmware/main/hal/u8g2_esp32_hal.c
Normal file
262
firmware/main/hal/u8g2_esp32_hal.c
Normal file
@@ -0,0 +1,262 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "u8g2_esp32_hal.h"
|
||||
|
||||
static const char* TAG = "u8g2_hal";
|
||||
static const unsigned int I2C_TIMEOUT_MS = 1000;
|
||||
|
||||
static spi_device_handle_t handle_spi; // SPI handle.
|
||||
static i2c_cmd_handle_t handle_i2c; // I2C handle.
|
||||
static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data.
|
||||
|
||||
#define HOST SPI2_HOST
|
||||
|
||||
#undef ESP_ERROR_CHECK
|
||||
#define ESP_ERROR_CHECK(x) \
|
||||
do { \
|
||||
esp_err_t rc = (x); \
|
||||
if (rc != ESP_OK) { \
|
||||
ESP_LOGE("err", "esp_err_t = %d", rc); \
|
||||
assert(0 && #x); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
/*
|
||||
* Initialze the ESP32 HAL.
|
||||
*/
|
||||
void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) {
|
||||
u8g2_esp32_hal = u8g2_esp32_hal_param;
|
||||
} // u8g2_esp32_hal_init
|
||||
|
||||
/*
|
||||
* HAL callback function as prescribed by the U8G2 library. This callback is
|
||||
* invoked to handle SPI communications.
|
||||
*/
|
||||
uint8_t u8g2_esp32_spi_byte_cb(u8x8_t* u8x8,
|
||||
uint8_t msg,
|
||||
uint8_t arg_int,
|
||||
void* arg_ptr) {
|
||||
ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p",
|
||||
msg, arg_int, arg_ptr);
|
||||
switch (msg) {
|
||||
case U8X8_MSG_BYTE_SET_DC:
|
||||
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.dc, arg_int);
|
||||
}
|
||||
break;
|
||||
|
||||
case U8X8_MSG_BYTE_INIT: {
|
||||
if (u8g2_esp32_hal.bus.spi.clk == U8G2_ESP32_HAL_UNDEFINED ||
|
||||
u8g2_esp32_hal.bus.spi.mosi == U8G2_ESP32_HAL_UNDEFINED ||
|
||||
u8g2_esp32_hal.bus.spi.cs == U8G2_ESP32_HAL_UNDEFINED) {
|
||||
break;
|
||||
}
|
||||
|
||||
spi_bus_config_t bus_config;
|
||||
memset(&bus_config, 0, sizeof(spi_bus_config_t));
|
||||
bus_config.sclk_io_num = u8g2_esp32_hal.bus.spi.clk; // CLK
|
||||
bus_config.mosi_io_num = u8g2_esp32_hal.bus.spi.mosi; // MOSI
|
||||
bus_config.miso_io_num = GPIO_NUM_NC; // MISO
|
||||
bus_config.quadwp_io_num = GPIO_NUM_NC; // Not used
|
||||
bus_config.quadhd_io_num = GPIO_NUM_NC; // Not used
|
||||
// ESP_LOGI(TAG, "... Initializing bus.");
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(HOST, &bus_config, 1));
|
||||
|
||||
spi_device_interface_config_t dev_config;
|
||||
dev_config.address_bits = 0;
|
||||
dev_config.command_bits = 0;
|
||||
dev_config.dummy_bits = 0;
|
||||
dev_config.mode = 0;
|
||||
dev_config.duty_cycle_pos = 0;
|
||||
dev_config.cs_ena_posttrans = 0;
|
||||
dev_config.cs_ena_pretrans = 0;
|
||||
dev_config.clock_speed_hz = 10000;
|
||||
dev_config.spics_io_num = u8g2_esp32_hal.bus.spi.cs;
|
||||
dev_config.flags = 0;
|
||||
dev_config.queue_size = 200;
|
||||
dev_config.pre_cb = NULL;
|
||||
dev_config.post_cb = NULL;
|
||||
// ESP_LOGI(TAG, "... Adding device bus.");
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(HOST, &dev_config, &handle_spi));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case U8X8_MSG_BYTE_SEND: {
|
||||
spi_transaction_t trans_desc;
|
||||
trans_desc.addr = 0;
|
||||
trans_desc.cmd = 0;
|
||||
trans_desc.flags = 0;
|
||||
trans_desc.length = 8 * arg_int; // Number of bits NOT number of bytes.
|
||||
trans_desc.rxlength = 0;
|
||||
trans_desc.tx_buffer = arg_ptr;
|
||||
trans_desc.rx_buffer = NULL;
|
||||
|
||||
// ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int);
|
||||
ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} // u8g2_esp32_spi_byte_cb
|
||||
|
||||
/*
|
||||
* HAL callback function as prescribed by the U8G2 library. This callback is
|
||||
* invoked to handle I2C communications.
|
||||
*/
|
||||
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t* u8x8,
|
||||
uint8_t msg,
|
||||
uint8_t arg_int,
|
||||
void* arg_ptr) {
|
||||
ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg,
|
||||
arg_int, arg_ptr);
|
||||
|
||||
switch (msg) {
|
||||
case U8X8_MSG_BYTE_SET_DC: {
|
||||
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.dc, arg_int);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case U8X8_MSG_BYTE_INIT: {
|
||||
if (u8g2_esp32_hal.bus.i2c.sda == U8G2_ESP32_HAL_UNDEFINED ||
|
||||
u8g2_esp32_hal.bus.i2c.scl == U8G2_ESP32_HAL_UNDEFINED) {
|
||||
break;
|
||||
}
|
||||
|
||||
i2c_config_t conf = {0};
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.bus.i2c.sda);
|
||||
conf.sda_io_num = u8g2_esp32_hal.bus.i2c.sda;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.bus.i2c.scl);
|
||||
conf.scl_io_num = u8g2_esp32_hal.bus.i2c.scl;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ);
|
||||
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
|
||||
ESP_LOGI(TAG, "i2c_param_config %d", conf.mode);
|
||||
ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
|
||||
ESP_LOGI(TAG, "i2c_driver_install %d", I2C_MASTER_NUM);
|
||||
ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode,
|
||||
I2C_MASTER_RX_BUF_DISABLE,
|
||||
I2C_MASTER_TX_BUF_DISABLE, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
case U8X8_MSG_BYTE_SEND: {
|
||||
uint8_t* data_ptr = (uint8_t*)arg_ptr;
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE);
|
||||
|
||||
while (arg_int > 0) {
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN));
|
||||
data_ptr++;
|
||||
arg_int--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case U8X8_MSG_BYTE_START_TRANSFER: {
|
||||
uint8_t i2c_address = u8x8_GetI2CAddress(u8x8);
|
||||
handle_i2c = i2c_cmd_link_create();
|
||||
ESP_LOGD(TAG, "Start I2C transfer to %02X.", i2c_address >> 1);
|
||||
ESP_ERROR_CHECK(i2c_master_start(handle_i2c));
|
||||
ESP_ERROR_CHECK(i2c_master_write_byte(
|
||||
handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN));
|
||||
break;
|
||||
}
|
||||
|
||||
case U8X8_MSG_BYTE_END_TRANSFER: {
|
||||
ESP_LOGD(TAG, "End I2C transfer.");
|
||||
ESP_ERROR_CHECK(i2c_master_stop(handle_i2c));
|
||||
ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c,
|
||||
pdMS_TO_TICKS(I2C_TIMEOUT_MS)));
|
||||
i2c_cmd_link_delete(handle_i2c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} // u8g2_esp32_i2c_byte_cb
|
||||
|
||||
/*
|
||||
* HAL callback function as prescribed by the U8G2 library. This callback is
|
||||
* invoked to handle callbacks for GPIO and delay functions.
|
||||
*/
|
||||
uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t* u8x8,
|
||||
uint8_t msg,
|
||||
uint8_t arg_int,
|
||||
void* arg_ptr) {
|
||||
ESP_LOGD(TAG,
|
||||
"gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p",
|
||||
msg, arg_int, arg_ptr);
|
||||
|
||||
switch (msg) {
|
||||
// Initialize the GPIO and DELAY HAL functions. If the pins for DC and
|
||||
// RESET have been specified then we define those pins as GPIO outputs.
|
||||
case U8X8_MSG_GPIO_AND_DELAY_INIT: {
|
||||
uint64_t bitmask = 0;
|
||||
if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
bitmask = bitmask | (1ull << u8g2_esp32_hal.dc);
|
||||
}
|
||||
if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
bitmask = bitmask | (1ull << u8g2_esp32_hal.reset);
|
||||
}
|
||||
if (u8g2_esp32_hal.bus.spi.cs != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
bitmask = bitmask | (1ull << u8g2_esp32_hal.bus.spi.cs);
|
||||
}
|
||||
|
||||
if (bitmask == 0) {
|
||||
break;
|
||||
}
|
||||
gpio_config_t gpioConfig;
|
||||
gpioConfig.pin_bit_mask = bitmask;
|
||||
gpioConfig.mode = GPIO_MODE_OUTPUT;
|
||||
gpioConfig.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpioConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpioConfig.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_config(&gpioConfig);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the GPIO reset pin to the value passed in through arg_int.
|
||||
case U8X8_MSG_GPIO_RESET:
|
||||
if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.reset, arg_int);
|
||||
}
|
||||
break;
|
||||
// Set the GPIO client select pin to the value passed in through arg_int.
|
||||
case U8X8_MSG_GPIO_CS:
|
||||
if (u8g2_esp32_hal.bus.spi.cs != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.bus.spi.cs, arg_int);
|
||||
}
|
||||
break;
|
||||
// Set the Software I²C pin to the value passed in through arg_int.
|
||||
case U8X8_MSG_GPIO_I2C_CLOCK:
|
||||
if (u8g2_esp32_hal.bus.i2c.scl != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.bus.i2c.scl, arg_int);
|
||||
// printf("%c",(arg_int==1?'C':'c'));
|
||||
}
|
||||
break;
|
||||
// Set the Software I²C pin to the value passed in through arg_int.
|
||||
case U8X8_MSG_GPIO_I2C_DATA:
|
||||
if (u8g2_esp32_hal.bus.i2c.sda != U8G2_ESP32_HAL_UNDEFINED) {
|
||||
gpio_set_level(u8g2_esp32_hal.bus.i2c.sda, arg_int);
|
||||
// printf("%c",(arg_int==1?'D':'d'));
|
||||
}
|
||||
break;
|
||||
|
||||
// Delay for the number of milliseconds passed in through arg_int.
|
||||
case U8X8_MSG_DELAY_MILLI:
|
||||
vTaskDelay(arg_int / portTICK_PERIOD_MS);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
} // u8g2_esp32_gpio_and_delay_cb
|
||||
92
firmware/main/hal/u8g2_esp32_hal.h
Normal file
92
firmware/main/hal/u8g2_esp32_hal.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef U8G2_ESP32_HAL_H
|
||||
#define U8G2_ESP32_HAL_H
|
||||
/*
|
||||
* u8g2_esp32_hal.h
|
||||
*
|
||||
* Created on: Feb 12, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef U8G2_ESP32_HAL_H_
|
||||
#define U8G2_ESP32_HAL_H_
|
||||
#include "u8g2.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
#define U8G2_ESP32_HAL_UNDEFINED GPIO_NUM_NC
|
||||
|
||||
#if SOC_I2C_NUM > 1
|
||||
#define I2C_MASTER_NUM I2C_NUM_1 // I2C port number for master dev
|
||||
#else
|
||||
#define I2C_MASTER_NUM I2C_NUM_0 // I2C port number for master dev
|
||||
#endif
|
||||
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0 // I2C master do not need buffer
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0 // I2C master do not need buffer
|
||||
#define I2C_MASTER_FREQ_HZ 400000 // I2C master clock frequency
|
||||
#define ACK_CHECK_EN 0x1 // I2C master will check ack from slave
|
||||
#define ACK_CHECK_DIS 0x0 // I2C master will not check ack from slave
|
||||
|
||||
/** @public
|
||||
* HAL configuration structure.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
/* SPI settings. */
|
||||
struct
|
||||
{
|
||||
/* GPIO num for clock. */
|
||||
gpio_num_t clk;
|
||||
/* GPIO num for SPI mosi. */
|
||||
gpio_num_t mosi;
|
||||
/* GPIO num for SPI slave/chip select. */
|
||||
gpio_num_t cs;
|
||||
} spi;
|
||||
/* I2C settings. */
|
||||
struct
|
||||
{
|
||||
/* GPIO num for I2C data. */
|
||||
gpio_num_t sda;
|
||||
/* GPIO num for I2C clock. */
|
||||
gpio_num_t scl;
|
||||
} i2c;
|
||||
} bus;
|
||||
/* GPIO num for reset. */
|
||||
gpio_num_t reset;
|
||||
/* GPIO num for DC. */
|
||||
gpio_num_t dc;
|
||||
} u8g2_esp32_hal_t;
|
||||
|
||||
/**
|
||||
* Construct a default HAL configuration with all fields undefined.
|
||||
*/
|
||||
#define U8G2_ESP32_HAL_DEFAULT \
|
||||
{.bus = {.spi = {.clk = U8G2_ESP32_HAL_UNDEFINED, \
|
||||
.mosi = U8G2_ESP32_HAL_UNDEFINED, \
|
||||
.cs = U8G2_ESP32_HAL_UNDEFINED}}, \
|
||||
.reset = U8G2_ESP32_HAL_UNDEFINED, \
|
||||
.dc = U8G2_ESP32_HAL_UNDEFINED}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/**
|
||||
* Initialize the HAL with the given configuration.
|
||||
*
|
||||
* @see u8g2_esp32_hal_t
|
||||
* @see U8G2_ESP32_HAL_DEFAULT
|
||||
*/
|
||||
void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param);
|
||||
uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* U8G2_ESP32_HAL_H_ */
|
||||
|
||||
#endif
|
||||
6
firmware/main/idf_component.yml
Executable file
6
firmware/main/idf_component.yml
Executable file
@@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
u8g2:
|
||||
git: https://github.com/olikraus/u8g2.git
|
||||
# u8g2_hal:
|
||||
# git: https://github.com/mkfrey/u8g2-hal-esp-idf.git
|
||||
espressif/button: ^4.1.3
|
||||
20
firmware/main/main.cpp
Normal file
20
firmware/main/main.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "app_task.h"
|
||||
#include "esp_event.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "led_manager.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
void app_main(void)
|
||||
{
|
||||
xTaskCreatePinnedToCore(app_task, "main_loop", 4096, NULL, tskIDLE_PRIORITY + 1, NULL, portNUM_PROCESSORS - 1);
|
||||
|
||||
wled_init();
|
||||
register_handler();
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
158
firmware/main/setup.c
Normal file
158
firmware/main/setup.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "setup.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
|
||||
#include "u8g2.h"
|
||||
#include "u8g2_esp32_hal.h"
|
||||
|
||||
#include "button_handling.h"
|
||||
#include "common.h"
|
||||
|
||||
#define PIN_SDA GPIO_NUM_35
|
||||
#define PIN_SCL GPIO_NUM_36
|
||||
#define PIN_RST GPIO_NUM_NC
|
||||
|
||||
#define WLED_GPIO GPIO_NUM_47
|
||||
#define WLED_RMT_CHANNEL RMT_CHANNEL_0
|
||||
#define WLED_RESOLUTION_HZ (10000000)
|
||||
#define WLED_ON_DURATION_MS (100)
|
||||
#define NUM_LEDS (1)
|
||||
|
||||
uint8_t last_value = 0;
|
||||
|
||||
static const char *TAG = "main";
|
||||
|
||||
extern QueueHandle_t buttonQueue;
|
||||
|
||||
rmt_channel_handle_t rmt_led_chan = NULL;
|
||||
rmt_encoder_handle_t rmt_led_encoder = NULL;
|
||||
bool wled_is_on = false;
|
||||
int64_t wled_turn_off_time = 0;
|
||||
|
||||
u8g2_t u8g2;
|
||||
uint8_t received_signal;
|
||||
|
||||
static void init_rmt_ws2812b(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initialize RMT TX Channel for WS2812B on GPIO %d", WLED_GPIO);
|
||||
rmt_tx_channel_config_t tx_chan_config = {
|
||||
.gpio_num = WLED_GPIO,
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = WLED_RESOLUTION_HZ,
|
||||
.mem_block_symbols = 64,
|
||||
.trans_queue_depth = 4,
|
||||
.intr_priority = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &rmt_led_chan));
|
||||
|
||||
ESP_LOGI(TAG, "Install RMT Bytes Encoder");
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||||
.bit0 = {.duration0 = 4, .level0 = 1, .duration1 = 8, .level1 = 0},
|
||||
.bit1 = {.duration0 = 8, .level0 = 1, .duration1 = 4, .level1 = 0},
|
||||
.flags = {.msb_first = 1}};
|
||||
ESP_ERROR_CHECK(rmt_new_bytes_encoder(&bytes_encoder_config, &rmt_led_encoder));
|
||||
|
||||
ESP_LOGI(TAG, "Activate RMT TX Kanal");
|
||||
ESP_ERROR_CHECK(rmt_enable(rmt_led_chan));
|
||||
}
|
||||
|
||||
static void set_wled_color(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
if (!rmt_led_chan || !rmt_led_encoder)
|
||||
{
|
||||
ESP_LOGE(TAG, "RMT Channel or Encoder not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t buffer_size = 3 * NUM_LEDS;
|
||||
uint8_t led_data[buffer_size];
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
led_data[i * 3 + 0] = g;
|
||||
led_data[i * 3 + 1] = r;
|
||||
led_data[i * 3 + 2] = b;
|
||||
}
|
||||
rmt_transmit_config_t tx_config = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
esp_err_t ret = rmt_transmit(rmt_led_chan, rmt_led_encoder, led_data, sizeof(led_data), &tx_config);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "RMT Transmit failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
ESP_ERROR_CHECK(rmt_tx_wait_all_done(rmt_led_chan, pdMS_TO_TICKS(100)));
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
setup_buttons();
|
||||
|
||||
u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
|
||||
u8g2_esp32_hal.bus.i2c.sda = PIN_SDA;
|
||||
u8g2_esp32_hal.bus.i2c.scl = PIN_SCL;
|
||||
u8g2_esp32_hal.reset = PIN_RST;
|
||||
u8g2_esp32_hal_init(u8g2_esp32_hal);
|
||||
|
||||
u8g2_Setup_sh1106_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);
|
||||
u8x8_SetI2CAddress(&u8g2.u8x8, 0x3C * 2);
|
||||
|
||||
ESP_LOGI(TAG, "u8g2_InitDisplay");
|
||||
u8g2_InitDisplay(&u8g2);
|
||||
|
||||
ESP_LOGI(TAG, "u8g2_SetPowerSave");
|
||||
u8g2_SetPowerSave(&u8g2, 0);
|
||||
|
||||
init_rmt_ws2812b();
|
||||
set_wled_color(0, 0, 0);
|
||||
|
||||
ESP_LOGI(TAG, "Start of main loop. Waiting for button press...");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);
|
||||
u8g2_DrawStr(&u8g2, 5, 20, "Ready!");
|
||||
char count_str[50];
|
||||
snprintf(count_str, sizeof(count_str), "Signal Value: %u", last_value);
|
||||
u8g2_DrawStr(&u8g2, 5, 45, count_str);
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
|
||||
if (xQueueReceive(buttonQueue, &received_signal, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
ESP_LOGI(TAG, "Button event from Queue received!");
|
||||
|
||||
last_value = received_signal;
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
u8g2_DrawStr(&u8g2, 5, 20, "Pressed!");
|
||||
u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);
|
||||
char count_str[50];
|
||||
snprintf(count_str, sizeof(count_str), "Signal Value: %u", last_value);
|
||||
u8g2_DrawStr(&u8g2, 5, 45, count_str);
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
ESP_LOGI(TAG, "Display refreshed with signal value: %u", last_value);
|
||||
|
||||
ESP_LOGD(TAG, "Switch WLED ON");
|
||||
set_wled_color(255, 0, 255);
|
||||
wled_is_on = true;
|
||||
wled_turn_off_time = esp_timer_get_time() + (WLED_ON_DURATION_MS * 1000);
|
||||
}
|
||||
|
||||
if (wled_is_on && esp_timer_get_time() >= wled_turn_off_time)
|
||||
{
|
||||
ESP_LOGD(TAG, "Switch WLED OFF");
|
||||
set_wled_color(0, 0, 0);
|
||||
wled_is_on = false;
|
||||
}
|
||||
}
|
||||
71
firmware/main/setup.h
Normal file
71
firmware/main/setup.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @file setup.h
|
||||
* @brief System initialization and main loop declarations for embedded application
|
||||
* @details This header defines the core system initialization and main loop functions
|
||||
* required for embedded ESP32 applications. It provides the essential entry
|
||||
* points for hardware setup, system configuration, and continuous operation
|
||||
* management following standard embedded system patterns.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/**
|
||||
* @brief Initializes all system components and hardware peripherals
|
||||
*
|
||||
* @details This function performs complete system initialization including:
|
||||
* - Hardware peripheral configuration (GPIO, I2C, SPI, etc.)
|
||||
* - Display system initialization
|
||||
* - Button and input device setup
|
||||
* - Communication subsystem initialization
|
||||
* - Memory and storage system preparation
|
||||
* - Application-specific component initialization
|
||||
*
|
||||
* This function is called once during system startup before entering
|
||||
* the main application loop. It ensures all required subsystems are
|
||||
* properly configured and ready for operation.
|
||||
*
|
||||
* @pre System must be in a clean startup state
|
||||
* @post All system components are initialized and ready for operation
|
||||
*
|
||||
* @note This function must complete successfully before loop() is called
|
||||
* @note Any initialization failures should be handled gracefully with
|
||||
* appropriate error reporting or system recovery
|
||||
*
|
||||
* @see loop() for the main application execution function
|
||||
*/
|
||||
void setup(void);
|
||||
|
||||
/**
|
||||
* @brief Main application execution loop for continuous operation
|
||||
*
|
||||
* @details This function contains the main application logic that executes
|
||||
* continuously after system initialization. It typically handles:
|
||||
* - User input processing and event handling
|
||||
* - Display updates and rendering operations
|
||||
* - System state management and transitions
|
||||
* - Background tasks and periodic operations
|
||||
* - Communication handling and data processing
|
||||
* - Power management and system monitoring
|
||||
*
|
||||
* The loop function is called repeatedly in an infinite cycle, providing
|
||||
* the main execution context for the embedded application. It should be
|
||||
* designed to execute efficiently without blocking to maintain system
|
||||
* responsiveness.
|
||||
*
|
||||
* @note This function runs continuously and should not block indefinitely
|
||||
* @note All operations within this function should be non-blocking or
|
||||
* use appropriate task scheduling for time-consuming operations
|
||||
* @note The function should handle all runtime errors gracefully
|
||||
*
|
||||
* @see setup() for system initialization before loop execution
|
||||
*/
|
||||
void loop(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
7
firmware/partitions.csv
Normal file
7
firmware/partitions.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# Name , Type , SubType , Offset , Size , Flags
|
||||
nvs , data , nvs , 0x9000 , 20k ,
|
||||
otadata , data , ota , 0xe000 , 8k ,
|
||||
app0 , app , ota_0 , 0x10000 , 1024k ,
|
||||
app1 , app , ota_1 , , 1024k ,
|
||||
spiffs , data , spiffs , , 1536k ,
|
||||
coredump , data , coredump , , 64k ,
|
||||
|
19
firmware/sdkconfig.defaults
Executable file
19
firmware/sdkconfig.defaults
Executable file
@@ -0,0 +1,19 @@
|
||||
# activate Bluetooth Low Energy (BLE)
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
|
||||
# Logging
|
||||
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||
CONFIG_LOG_MAXIMUM_LEVEL=3
|
||||
|
||||
# Flash Size
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||
|
||||
# Partitions
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
2
firmware/sdkconfig.defaults.esp32c3
Normal file
2
firmware/sdkconfig.defaults.esp32c3
Normal file
@@ -0,0 +1,2 @@
|
||||
# default ESP target
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
2
firmware/sdkconfig.defaults.esp32h2
Normal file
2
firmware/sdkconfig.defaults.esp32h2
Normal file
@@ -0,0 +1,2 @@
|
||||
# default ESP target
|
||||
CONFIG_IDF_TARGET="esp32h2"
|
||||
2
firmware/sdkconfig.defaults.esp32p4
Normal file
2
firmware/sdkconfig.defaults.esp32p4
Normal file
@@ -0,0 +1,2 @@
|
||||
# default ESP target
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
2
firmware/sdkconfig.defaults.esp32s3
Normal file
2
firmware/sdkconfig.defaults.esp32s3
Normal file
@@ -0,0 +1,2 @@
|
||||
# default ESP target
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
0
firmware/sdkconfig.release
Normal file
0
firmware/sdkconfig.release
Normal file
26
firmware/src/Common.cpp
Normal file
26
firmware/src/Common.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
auto CreateWindow(const char *title, const int width, const int height) -> Window *
|
||||
{
|
||||
|
||||
constexpr uint32_t window_flag = SDL_WINDOW_HIDDEN;
|
||||
const auto window = SDL_CreateWindow(title, width, height, window_flag);
|
||||
if (window == nullptr)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
|
||||
if (const auto renderer = SDL_CreateRendererWithProperties(props); renderer == nullptr)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create renderer", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Window(window);
|
||||
}
|
||||
60
firmware/src/Common.h
Normal file
60
firmware/src/Common.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @file Common.h
|
||||
* @brief Common utility functions and window management for application framework
|
||||
* @details This header defines common utility functions that are shared across
|
||||
* the application framework. It provides essential functionality for
|
||||
* window creation and management, serving as a bridge between the
|
||||
* application layer and the underlying windowing system.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "model/Window.h"
|
||||
|
||||
/**
|
||||
* @brief Creates a new window with specified title and dimensions
|
||||
* @param title Null-terminated string containing the window title text
|
||||
* @param width Window width in pixels
|
||||
* @param height Window height in pixels
|
||||
* @return Pointer to the newly created Window object, or nullptr on failure
|
||||
*
|
||||
* @pre title must not be nullptr and should contain valid display text
|
||||
* @pre width and height must be positive values within system display limits
|
||||
* @post A new Window object is allocated and initialized with the specified parameters
|
||||
* @post The returned Window pointer is ready for use with window management functions
|
||||
*
|
||||
* @details This function creates a new Window instance with the specified
|
||||
* title and dimensions. It handles the underlying window system
|
||||
* initialization, memory allocation, and setup required to create
|
||||
* a functional window object.
|
||||
*
|
||||
* The window creation process includes:
|
||||
* - Memory allocation for the Window structure
|
||||
* - Initialization of window properties (title, dimensions, state)
|
||||
* - Registration with the window management system
|
||||
* - Setup of default window behavior and event handling
|
||||
*
|
||||
* The returned window pointer can be used with other window management
|
||||
* functions to display content, handle events, and manage the window
|
||||
* lifecycle. The caller is responsible for properly managing the window
|
||||
* lifetime and ensuring proper cleanup when the window is no longer needed.
|
||||
*
|
||||
* @note The returned pointer must be properly managed by the caller
|
||||
* @note Window resources should be freed when no longer needed
|
||||
* @note The title string is copied internally and can be safely modified
|
||||
* or freed after this function returns
|
||||
*
|
||||
* @see Window class for window object interface and methods
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* auto* window = CreateWindow("System Control", 800, 600);
|
||||
* if (window != nullptr) {
|
||||
* // Use the window...
|
||||
* // Clean up when done
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
auto CreateWindow(const char *title, int width, int height) -> Window *;
|
||||
10
firmware/src/Version.h
Normal file
10
firmware/src/Version.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "string"
|
||||
|
||||
const std::string MyProject = "system_control";
|
||||
const std::string MyProjectVersion = "0.0.1";
|
||||
constexpr uint8_t MyProjectVersionMajor = 0;
|
||||
constexpr uint8_t MyProjectVersionMinor = 0;
|
||||
constexpr uint8_t MyProjectVersionPatch = 1;
|
||||
const std::string MyProjectBuildDate = "";
|
||||
10
firmware/src/Version.h.in
Normal file
10
firmware/src/Version.h.in
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "string"
|
||||
|
||||
const std::string MyProject = "@PROJECT_NAME@";
|
||||
const std::string MyProjectVersion = "@PROJECT_VERSION@";
|
||||
constexpr uint8_t MyProjectVersionMajor = @PROJECT_VERSION_MAJOR@;
|
||||
constexpr uint8_t MyProjectVersionMinor = @PROJECT_VERSION_MINOR@;
|
||||
constexpr uint8_t MyProjectVersionPatch = @PROJECT_VERSION_PATCH@;
|
||||
const std::string MyProjectBuildDate = "@PROJECT_BUILD_DATE@";
|
||||
110
firmware/src/debug/debug_overlay.cpp
Normal file
110
firmware/src/debug/debug_overlay.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "debug/debug_overlay.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Matrix.h"
|
||||
#include "Version.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include <imgui_impl_sdlrenderer3.h>
|
||||
|
||||
namespace DebugOverlay
|
||||
{
|
||||
void Init(const AppContext *context)
|
||||
{
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io{ImGui::GetIO()};
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(context->MainWindow(), context->MainRenderer());
|
||||
ImGui_ImplSDLRenderer3_Init(context->MainRenderer());
|
||||
}
|
||||
|
||||
void Update(AppContext *context, const SDL_Event *event)
|
||||
{
|
||||
ImGui_ImplSDL3_ProcessEvent(event);
|
||||
|
||||
if (show_led_matrix)
|
||||
{
|
||||
if (!context->LedMatrixRenderer())
|
||||
{
|
||||
const auto win = CreateWindow("LED Matrix", width * 50, height * 50);
|
||||
SDL_SetWindowFocusable(win->window(), false);
|
||||
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
|
||||
SDL_SetWindowPosition(win->window(), 0, 0);
|
||||
SDL_ShowWindow(win->window());
|
||||
|
||||
const auto windowId = SDL_GetWindowID(win->window());
|
||||
context->SetMatrix(new Matrix(windowId, win->renderer(), width, height));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context->LedMatrixRenderer())
|
||||
{
|
||||
int window_count = 0;
|
||||
if (SDL_Window **windows = SDL_GetWindows(&window_count))
|
||||
{
|
||||
for (int i = 0; i < window_count; ++i)
|
||||
{
|
||||
if (SDL_Window *window = windows[i]; context->LedMatrixId() == SDL_GetWindowID(window))
|
||||
{
|
||||
SDL_DestroyRenderer(context->LedMatrixRenderer());
|
||||
SDL_DestroyWindow(window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(windows);
|
||||
}
|
||||
|
||||
SDL_DestroyRenderer(context->LedMatrixRenderer());
|
||||
|
||||
context->SetMatrix(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render(const AppContext *context)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (show_debug_window && ImGui::BeginMainMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Config"))
|
||||
{
|
||||
ImGui::Checkbox("Show LED Matrix", &show_led_matrix);
|
||||
ImGui::Checkbox("Show Unhandled Events", &show_unhandled_events);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help"))
|
||||
{
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::SeparatorText("App Info");
|
||||
ImGui::Text("Project: %s", MyProject.c_str());
|
||||
ImGui::Text("Version: %s", MyProjectVersion.c_str());
|
||||
ImGui::Text("Build Date: %s", MyProjectBuildDate.c_str());
|
||||
ImGui::Text("ImGui Version: %s", ImGui::GetVersion());
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), context->MainRenderer());
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
} // namespace DebugOverlay
|
||||
23
firmware/src/debug/debug_overlay.h
Normal file
23
firmware/src/debug/debug_overlay.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "model/AppContext.h"
|
||||
|
||||
namespace DebugOverlay
|
||||
{
|
||||
inline bool show_debug_window = false;
|
||||
inline bool show_unhandled_events = false;
|
||||
inline bool show_led_matrix = false;
|
||||
|
||||
constexpr auto width = 8;
|
||||
constexpr auto height = 8;
|
||||
|
||||
void Init(const AppContext *context);
|
||||
|
||||
void Update(AppContext *context, const SDL_Event *event);
|
||||
|
||||
void Render(const AppContext *context);
|
||||
|
||||
void Cleanup();
|
||||
} // namespace DebugOverlay
|
||||
11
firmware/src/hal/u8g2_hal_sdl.h
Normal file
11
firmware/src/hal/u8g2_hal_sdl.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "u8g2.h"
|
||||
|
||||
#define U8G2_SCREEN_WIDTH (128)
|
||||
#define U8G2_SCREEN_HEIGHT (64)
|
||||
#define U8G2_SCREEN_FACTOR (3)
|
||||
#define U8G2_SCREEN_PADDING (25)
|
||||
|
||||
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user