refactor(simu): use SDL & ImGui (#6233)
Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> Co-authored-by: Malte Langermann <malte@langermann.net>
This commit is contained in:
co-authored by
raphaelcoeffic
Malte Langermann
parent
14287cb91d
commit
4eddda034d
@@ -0,0 +1,26 @@
|
||||
# Fetch imgui source code from Github
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
imgui
|
||||
GIT_REPOSITORY https://github.com/ocornut/imgui
|
||||
GIT_TAG dbb5eeaadffb6a3ba6a60de1290312e5802dba5a # v1.91.8
|
||||
SOURCE_DIR imgui
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(imgui)
|
||||
|
||||
add_library(imgui STATIC
|
||||
imgui/imgui.cpp
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_tables.cpp
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/backends/imgui_impl_sdl2.cpp
|
||||
imgui/backends/imgui_impl_sdlrenderer2.cpp
|
||||
)
|
||||
|
||||
target_include_directories(imgui PUBLIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}/imgui
|
||||
${CMAKE_CURRENT_BINARY_DIR}/imgui/backends
|
||||
)
|
||||
@@ -1,63 +0,0 @@
|
||||
# Imported from the DeskVox project (LPGL 2.1)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
set(hints
|
||||
$ENV{LIB_BASE_PATH}/fox
|
||||
)
|
||||
|
||||
set(paths
|
||||
/usr
|
||||
/usr/local
|
||||
)
|
||||
|
||||
find_path(FOX_INCLUDE_DIR
|
||||
NAMES
|
||||
fx.h
|
||||
HINTS
|
||||
${hints}
|
||||
PATHS
|
||||
${paths}
|
||||
PATH_SUFFIXES
|
||||
include
|
||||
include/fox
|
||||
include/fox-1.6
|
||||
)
|
||||
|
||||
find_library(FOX_LIBRARY
|
||||
NAMES
|
||||
FOX-1.6
|
||||
fox-1.6
|
||||
HINTS
|
||||
${hints}
|
||||
PATHS
|
||||
${paths}
|
||||
PATH_SUFFIXES
|
||||
lib64
|
||||
lib
|
||||
)
|
||||
|
||||
find_library(FOX_LIBRARY_DEBUG
|
||||
NAMES
|
||||
FOXD-1.6
|
||||
foxd-1.6
|
||||
HINTS
|
||||
${hints}
|
||||
PATHS
|
||||
${paths}
|
||||
PATH_SUFFIXES
|
||||
lib64
|
||||
lib
|
||||
)
|
||||
|
||||
if(FOX_LIBRARY_DEBUG)
|
||||
set(FOX_LIBRARIES optimized ${FOX_LIBRARY} debug ${FOX_LIBRARY_DEBUG})
|
||||
else()
|
||||
set(FOX_LIBRARIES ${FOX_LIBRARY})
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(Fox
|
||||
DEFAULT_MSG
|
||||
FOX_INCLUDE_DIR
|
||||
FOX_LIBRARY
|
||||
)
|
||||
@@ -1,71 +0,0 @@
|
||||
# Find libphonon
|
||||
# Once done this will define
|
||||
#
|
||||
# PHONON_FOUND - system has Phonon Library
|
||||
# PHONON_INCLUDES - the Phonon include directory
|
||||
# PHONON_LIBS - link these to use Phonon
|
||||
# PHONON_VERSION - the version of the Phonon Library
|
||||
|
||||
# Copyright (c) 2008, Matthias Kretz <kretz@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
macro(_phonon_find_version)
|
||||
set(_phonon_namespace_header_file "${PHONON_INCLUDE_DIR}/phonon/phononnamespace.h")
|
||||
if (APPLE AND EXISTS "${PHONON_INCLUDE_DIR}/Headers/phononnamespace.h")
|
||||
set(_phonon_namespace_header_file "${PHONON_INCLUDE_DIR}/Headers/phononnamespace.h")
|
||||
endif (APPLE AND EXISTS "${PHONON_INCLUDE_DIR}/Headers/phononnamespace.h")
|
||||
file(READ ${_phonon_namespace_header_file} _phonon_header LIMIT 5000 OFFSET 1000)
|
||||
string(REGEX MATCH "define PHONON_VERSION_STR \"(4\\.[0-9]+\\.[0-9a-z]+)\"" _phonon_version_match "${_phonon_header}")
|
||||
set(PHONON_VERSION "${CMAKE_MATCH_1}")
|
||||
message(STATUS "Phonon Version: ${PHONON_VERSION}")
|
||||
endmacro(_phonon_find_version)
|
||||
|
||||
if(PHONON_FOUND)
|
||||
# Already found, nothing more to do except figuring out the version
|
||||
_phonon_find_version()
|
||||
else(PHONON_FOUND)
|
||||
if(PHONON_INCLUDE_DIR AND PHONON_LIBRARY)
|
||||
set(PHONON_FIND_QUIETLY TRUE)
|
||||
endif(PHONON_INCLUDE_DIR AND PHONON_LIBRARY)
|
||||
|
||||
# As discussed on kde-buildsystem: first look at CMAKE_PREFIX_PATH, then at the suggested PATHS (kde4 install dir)
|
||||
find_library(PHONON_LIBRARY NAMES phonon phonon4 PATHS ${KDE4_LIB_INSTALL_DIR} ${QT_LIBRARY_DIR} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
|
||||
# then at the default system locations (CMAKE_SYSTEM_PREFIX_PATH, i.e. /usr etc.)
|
||||
find_library(PHONON_LIBRARY NAMES phonon)
|
||||
|
||||
find_path(PHONON_INCLUDE_DIR NAMES phonon/phonon_export.h PATHS ${KDE4_INCLUDE_INSTALL_DIR} ${QT_INCLUDE_DIR} ${INCLUDE_INSTALL_DIR} ${QT_LIBRARY_DIR} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
|
||||
find_path(PHONON_INCLUDE_DIR NAMES phonon/phonon_export.h)
|
||||
|
||||
if(PHONON_INCLUDE_DIR AND PHONON_LIBRARY)
|
||||
set(PHONON_LIBS ${phonon_LIB_DEPENDS} ${PHONON_LIBRARY})
|
||||
set(PHONON_INCLUDES ${PHONON_INCLUDE_DIR}/KDE ${PHONON_INCLUDE_DIR})
|
||||
set(PHONON_FOUND TRUE)
|
||||
_phonon_find_version()
|
||||
else(PHONON_INCLUDE_DIR AND PHONON_LIBRARY)
|
||||
set(PHONON_FOUND FALSE)
|
||||
endif(PHONON_INCLUDE_DIR AND PHONON_LIBRARY)
|
||||
|
||||
if(PHONON_FOUND)
|
||||
if(NOT PHONON_FIND_QUIETLY)
|
||||
message(STATUS "Found Phonon: ${PHONON_LIBRARY}")
|
||||
message(STATUS "Found Phonon Includes: ${PHONON_INCLUDES}")
|
||||
endif(NOT PHONON_FIND_QUIETLY)
|
||||
else(PHONON_FOUND)
|
||||
if(Phonon_FIND_REQUIRED)
|
||||
if(NOT PHONON_INCLUDE_DIR)
|
||||
message(STATUS "Phonon includes NOT found!")
|
||||
endif(NOT PHONON_INCLUDE_DIR)
|
||||
if(NOT PHONON_LIBRARY)
|
||||
message(STATUS "Phonon library NOT found!")
|
||||
endif(NOT PHONON_LIBRARY)
|
||||
message(FATAL_ERROR "Phonon library or includes NOT found!")
|
||||
else(Phonon_FIND_REQUIRED)
|
||||
message(STATUS "Unable to find Phonon")
|
||||
endif(Phonon_FIND_REQUIRED)
|
||||
endif(PHONON_FOUND)
|
||||
|
||||
|
||||
mark_as_advanced(PHONON_INCLUDE_DIR PHONON_LIBRARY PHONON_INCLUDES)
|
||||
endif(PHONON_FOUND)
|
||||
@@ -1,68 +0,0 @@
|
||||
# Find Xerces-C
|
||||
# The following setings are defined
|
||||
# XERCESC_ROOT_DIR, the root of the include and lib directory
|
||||
# XERCESC_INCLUDE_DIR, the full path of the include dir (ADVANCED)
|
||||
# XERCESC_LIBRARIES, the name of the xerces-c library (ADVANCED)
|
||||
|
||||
# Look for a root installation
|
||||
IF( MSVC )
|
||||
SET( XERCES_WINDIR C:/Programs/xerces-vc )
|
||||
ELSE( )
|
||||
SET( XERCES_WINDIR C:/Programs/xerces )
|
||||
ENDIF( )
|
||||
|
||||
FIND_PATH(XERCESC_ROOT_DIR include/xercesc/parsers/SAXParser.hpp
|
||||
${XERCES_WINDIR}
|
||||
"C:/mingw/msys/1.0/local"
|
||||
"C:/Program Files/CodeSynthesis XSD 3.2"
|
||||
/usr
|
||||
/usr/local
|
||||
"C:/MinGW"
|
||||
$ENV{CODESYNTH}
|
||||
DOC "The root of an installed xerces-c installation"
|
||||
)
|
||||
|
||||
# try to find the header
|
||||
FIND_PATH(XERCESC_INCLUDE_DIR xercesc/parsers/SAXParser.hpp
|
||||
${XERCESC_ROOT_DIR}/include
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
)
|
||||
|
||||
# Find the library
|
||||
FIND_LIBRARY(XERCESC_LIBRARY
|
||||
NAMES xerces-c xerces-c_3
|
||||
PATHS
|
||||
${XERCESC_ROOT_DIR}/lib
|
||||
${XERCESC_ROOT_DIR}/lib/vc-9.0
|
||||
${XERCESC_ROOT_DIR}/lib64/vc-9.0
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
DOC "The name of the xerces-c library"
|
||||
)
|
||||
|
||||
IF (XERCESC_ROOT_DIR)
|
||||
IF (XERCESC_INCLUDE_DIR AND XERCESC_LIBRARY)
|
||||
SET (XERCESC_FOUND TRUE)
|
||||
SET (XERCESC_LIBRARIES "${XERCESC_LIBRARY}")
|
||||
# FIXME: There should be a better way of handling this?
|
||||
# FIXME: How can we test to see if the lib dir isn't
|
||||
# FIXME: one of the default dirs?
|
||||
LINK_DIRECTORIES(${XERCESC_ROOT_DIR}/lib)
|
||||
ENDIF (XERCESC_INCLUDE_DIR AND XERCESC_LIBRARY)
|
||||
ENDIF (XERCESC_ROOT_DIR)
|
||||
|
||||
IF (XERCESC_FOUND)
|
||||
IF (NOT XERCESC_FIND_QUIETLY)
|
||||
MESSAGE (STATUS " found xerces-c: ${XERCESC_LIBRARY}")
|
||||
ENDIF (NOT XERCESC_FIND_QUIETLY)
|
||||
ELSE (XERCESC_FOUND)
|
||||
IF (XERCESC_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find Xerces-C")
|
||||
ENDIF (XERCESC_FIND_REQUIRED)
|
||||
ENDIF (XERCESC_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
XERCESC_INCLUDE_DIR
|
||||
XERCESC_LIBRARY
|
||||
)
|
||||
@@ -1,54 +0,0 @@
|
||||
# Locate Xsd from code synthesis include paths and binary
|
||||
# Xsd can be found at http://codesynthesis.com/products/xsd/
|
||||
# Written by Frederic Heem, frederic.heem _at_ telsey.it
|
||||
|
||||
# This module defines
|
||||
# XSD_INCLUDE_DIR, where to find elements.hxx, etc.
|
||||
# XSD_EXECUTABLE, where is the xsd compiler
|
||||
# XSD_FOUND, If false, don't try to use xsd
|
||||
|
||||
FIND_PATH( XSD_INCLUDE_DIR xsd/cxx/parser/elements.hxx
|
||||
"/opt/local/xsd-3.3.0-i686-macosx/libxsd"
|
||||
"C:/Programs/xsd-3.3.0/libxsd"
|
||||
"C:/Program Files/CodeSynthesis XSD 3.2/include"
|
||||
"C:/mingw/xsd-3.3.0-i686-windows/libxsd"
|
||||
$ENV{XSDDIR}/include
|
||||
$ENV{CODESYNTH}/include
|
||||
/usr/local/include /usr/include
|
||||
$ENV{XSDDIR}/libxsd
|
||||
)
|
||||
|
||||
IF( WIN32 )
|
||||
SET( XSDCXX_FILENAME1 xsd-cxx.exe )
|
||||
ELSE( )
|
||||
SET( XSDCXX_FILENAME1 xsdcxx )
|
||||
SET( XSDCXX_FILENAME2 xsd )
|
||||
ENDIF( )
|
||||
|
||||
FIND_PROGRAM( XSDCXX_EXECUTABLE
|
||||
NAMES
|
||||
${XSDCXX_FILENAME1} ${XSDCXX_FILENAME2}
|
||||
PATHS
|
||||
"/opt/local/xsd-3.3.0-i686-macosx/bin"
|
||||
"C:/Programs/xsd-3.3.0/bin"
|
||||
"C:/mingw/xsd-3.3.0-i686-windows/bin"
|
||||
"C:/Program Files/CodeSynthesis XSD 3.2/bin"
|
||||
$ENV{XSDDIR}/bin
|
||||
/usr/local/bin
|
||||
/usr/bin
|
||||
$ENV{XSDDIR}/xsd
|
||||
)
|
||||
|
||||
MESSAGE(STATUS ${XSDCXX_EXECUTABLE})
|
||||
|
||||
# if the include and the program are found then we have it
|
||||
IF( XSD_INCLUDE_DIR )
|
||||
IF( XSDCXX_EXECUTABLE )
|
||||
SET( XSD_FOUND "YES" )
|
||||
ENDIF( XSDCXX_EXECUTABLE )
|
||||
ENDIF( XSD_INCLUDE_DIR )
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
XSD_INCLUDE_DIR
|
||||
XSDCXX_EXECUTABLE
|
||||
)
|
||||
@@ -28,6 +28,7 @@ endif()
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_definitions(-D_GLIBCXX_USE_C99=1) # proper to_string definition
|
||||
|
||||
|
||||
+17
-31
@@ -1,41 +1,27 @@
|
||||
|
||||
option(DISABLE_COMPANION "Disable building companion and simulators" OFF)
|
||||
|
||||
# if(WIN32)
|
||||
# set(WIN_EXTRA_LIBS_PATH "C:/Programs" CACHE PATH
|
||||
# "Base path to extra libs/headers on Windows (SDL & pthreads folders should be in here).")
|
||||
# list(APPEND CMAKE_PREFIX_PATH "${WIN_EXTRA_LIBS_PATH}" "${WIN_EXTRA_LIBS_PATH}/SDL") # hints for FindSDL
|
||||
# endif()
|
||||
|
||||
# libfox
|
||||
find_package(Fox QUIET) # QUIET not working on WIN32?
|
||||
if (FOX_FOUND)
|
||||
message(STATUS "Foxlib found at ${FOX_LIBRARY}")
|
||||
else()
|
||||
message("Libfox not found, simu target will not be available")
|
||||
endif()
|
||||
|
||||
if(NOT DISABLE_COMPANION)
|
||||
include(QtDefs)
|
||||
endif(NOT DISABLE_COMPANION)
|
||||
|
||||
if(Qt5Core_FOUND OR FOX_FOUND)
|
||||
set(SDL2_BUILDING_LIBRARY YES) # this prevents FindSDL from appending SDLmain lib to the results, which we don't want
|
||||
find_package("SDL2")
|
||||
if(SDL2_FOUND)
|
||||
# find_package("SDL2") does not set a variable holding the path to the location of the SDL2 shared library
|
||||
find_file(SDL2_LIB_PATH
|
||||
NAMES
|
||||
libSDL2.so
|
||||
SDL2.dll
|
||||
SDL2.dylib
|
||||
HINTS
|
||||
"/usr/lib/x86_64-linux-gnu"
|
||||
${SDL2_LIBRARY_PATH})
|
||||
message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "SDL2 not found! Simulator audio, and joystick inputs, will not work.")
|
||||
endif()
|
||||
# this prevents FindSDL from appending SDLmain lib to the results, which we don't want
|
||||
set(SDL2_BUILDING_LIBRARY YES)
|
||||
find_package("SDL2")
|
||||
|
||||
if(SDL2_FOUND)
|
||||
# find_package("SDL2") does not set a variable holding the path to the location of the SDL2 shared library
|
||||
find_file(SDL2_LIB_PATH
|
||||
NAMES
|
||||
libSDL2.so
|
||||
SDL2.dll
|
||||
SDL2.dylib
|
||||
HINTS
|
||||
"/usr/lib/x86_64-linux-gnu"
|
||||
${SDL2_LIBRARY_PATH})
|
||||
message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "SDL not found! Simulator audio, and joystick inputs, will not work.")
|
||||
endif()
|
||||
|
||||
if(Qt5Core_FOUND AND NOT DISABLE_COMPANION)
|
||||
|
||||
+8
-8
@@ -6,14 +6,14 @@ endif()
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
find_package(Qt5Core)
|
||||
find_package(Qt5Widgets)
|
||||
find_package(Qt5Xml)
|
||||
find_package(Qt5LinguistTools)
|
||||
find_package(Qt5PrintSupport)
|
||||
find_package(Qt5Multimedia)
|
||||
find_package(Qt5Svg)
|
||||
find_package(Qt5SerialPort)
|
||||
find_package(Qt5Core QUIET)
|
||||
find_package(Qt5Widgets QUIET)
|
||||
find_package(Qt5Xml QUIET)
|
||||
find_package(Qt5LinguistTools QUIET)
|
||||
find_package(Qt5PrintSupport QUIET)
|
||||
find_package(Qt5Multimedia QUIET)
|
||||
find_package(Qt5Svg QUIET)
|
||||
find_package(Qt5SerialPort QUIET)
|
||||
|
||||
if(Qt5Core_FOUND)
|
||||
message(STATUS "Qt Version: ${Qt5Core_VERSION}")
|
||||
|
||||
@@ -28,9 +28,6 @@ option(SEMIHOSTING "Enable debugger semihosting" OFF)
|
||||
option(JITTER_MEASURE "Enable ADC jitter measurement" OFF)
|
||||
option(WATCHDOG "Enable hardware Watchdog" ON)
|
||||
option(ASTERISK "Enable asterisk icon (test only firmware)" OFF)
|
||||
if(SDL2_FOUND)
|
||||
option(SIMU_AUDIO "Enable simulator audio." ON)
|
||||
endif()
|
||||
option(LUA "Enable LUA support" ON)
|
||||
option(SIMU_DISKIO "Enable disk IO simulation in simulator. Simulator will use FatFs module and simulated IO layer that uses \"./sdcard.image\" file as image of SD card. This file must contain whole SD card from first to last sector" OFF)
|
||||
option(SIMU_LUA_COMPILER "Pre-compile and save Lua scripts in simulator." ON)
|
||||
@@ -245,10 +242,6 @@ if(WATCHDOG AND NOT NATIVE_BUILD)
|
||||
add_definitions(-DUSE_WATCHDOG)
|
||||
endif()
|
||||
|
||||
if(SIMU_AUDIO)
|
||||
add_definitions(-DSIMU_AUDIO)
|
||||
endif()
|
||||
|
||||
if(SIMU_DISKIO)
|
||||
add_definitions(-DSIMU_DISKIO)
|
||||
endif()
|
||||
@@ -493,10 +486,6 @@ endif()
|
||||
|
||||
if(NATIVE_BUILD)
|
||||
set(RADIOLIB_NATIVE_SRC ${SRC})
|
||||
if(SIMU_DISKIO)
|
||||
set(RADIOLIB_NATIVE_SRC ${RADIOLIB_NATIVE_SRC}
|
||||
${FATFS_DIR}/ff.c ${FATFS_DIR}/ffunicode.c)
|
||||
endif()
|
||||
|
||||
# Pack all radio sources into an object lib
|
||||
add_library(radiolib_native OBJECT EXCLUDE_FROM_ALL
|
||||
|
||||
+24
-12
@@ -370,6 +370,17 @@ AudioQueue::AudioQueue()
|
||||
|
||||
#define CODEC_ID_PCM_S16LE 1
|
||||
|
||||
|
||||
static void _audio_lock()
|
||||
{
|
||||
mutex_lock(&audioMutex);
|
||||
}
|
||||
|
||||
static void _audio_unlock()
|
||||
{
|
||||
mutex_unlock(&audioMutex);
|
||||
}
|
||||
|
||||
#if !defined(__SSAT)
|
||||
#define _sat_s16(x) ((int16_t)limit<int32_t>(INT16_MIN, (x), INT16_MAX))
|
||||
#else
|
||||
@@ -613,9 +624,9 @@ void AudioQueue::wakeup()
|
||||
|
||||
// mix the normal context (tones and wavs)
|
||||
if (normalContext.isEmpty() && !fragmentsFifo.empty()) {
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
normalContext.setFragment(fragmentsFifo.get());
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
result = normalContext.mixBuffer(buffer, g_eeGeneral.beepVolume, g_eeGeneral.wavVolume, fade);
|
||||
if (result > 0) {
|
||||
@@ -696,10 +707,11 @@ bool AudioQueue::isPlaying(uint8_t id)
|
||||
void AudioQueue::playTone(uint16_t freq, uint16_t len, uint16_t pause, uint8_t flags, int8_t freqIncr, int8_t fragmentVolume)
|
||||
{
|
||||
#if defined(SIMU) && !defined(SIMU_AUDIO)
|
||||
#warning "NO audio"
|
||||
return;
|
||||
#endif
|
||||
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
|
||||
freq = limit<uint16_t>(BEEP_MIN_FREQ, freq, BEEP_MAX_FREQ);
|
||||
|
||||
@@ -722,7 +734,7 @@ void AudioQueue::playTone(uint16_t freq, uint16_t len, uint16_t pause, uint8_t f
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
|
||||
void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8_t fragmentVolume)
|
||||
@@ -745,7 +757,7 @@ void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
|
||||
if (flags & PLAY_BACKGROUND) {
|
||||
backgroundContext.clear();
|
||||
@@ -755,7 +767,7 @@ void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8
|
||||
fragmentsFifo.push(AudioFragment(filename, flags & 0x0f, fragmentVolume, id));
|
||||
}
|
||||
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
|
||||
void AudioQueue::stopPlay(uint8_t id)
|
||||
@@ -768,12 +780,12 @@ void AudioQueue::stopPlay(uint8_t id)
|
||||
return;
|
||||
#endif
|
||||
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
|
||||
fragmentsFifo.removePromptById(id);
|
||||
backgroundContext.stop(id);
|
||||
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
|
||||
void AudioQueue::stopSD()
|
||||
@@ -786,19 +798,19 @@ void AudioQueue::stopSD()
|
||||
void AudioQueue::stopAll()
|
||||
{
|
||||
flush();
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
priorityContext.clear();
|
||||
normalContext.clear();
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
|
||||
void AudioQueue::flush()
|
||||
{
|
||||
mutex_lock(&audioMutex);
|
||||
_audio_lock();
|
||||
fragmentsFifo.clear();
|
||||
varioContext.clear();
|
||||
backgroundContext.clear();
|
||||
mutex_unlock(&audioMutex);
|
||||
_audio_unlock();
|
||||
}
|
||||
|
||||
void audioPlay(unsigned int index, uint8_t id)
|
||||
|
||||
@@ -469,7 +469,6 @@ enum {
|
||||
void codecsInit();
|
||||
void audioEvent(unsigned int index);
|
||||
void audioPlay(unsigned int index, uint8_t id=0);
|
||||
void audioStart();
|
||||
|
||||
#if defined(AUDIO) && defined(BUZZER)
|
||||
#define AUDIO_BUZZER(a, b) do { a; b; } while(0)
|
||||
|
||||
@@ -76,7 +76,7 @@ void menuRadioDiagFS(event_t event)
|
||||
lcdDrawText(FS_1ST_COLUMN + 7, y, getFSPhysicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP);
|
||||
lcdDrawText(FS_2ND_COLUMN + 5, y, getFSLogicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP);
|
||||
#if defined(FUNCTION_SWITCHES_RGB_LEDS)
|
||||
lcdDrawText(FS_3RD_COLUMN, y, STR_FS_COLOR_LIST[getRGBColorIndex(rgbGetLedColor(i))], 0);
|
||||
lcdDrawText(FS_3RD_COLUMN, y, STR_FS_COLOR_LIST[getRGBColorIndex(fsGetLedRGB(i))], 0);
|
||||
#else
|
||||
lcdDrawText(FS_3RD_COLUMN, y, STR_OFFON[fsLedState(i)]);
|
||||
#endif
|
||||
|
||||
@@ -102,11 +102,7 @@ void waitSplash()
|
||||
{
|
||||
// Handle color splash screen
|
||||
if (splashStartTime) {
|
||||
#if defined(SIMU)
|
||||
// Simulator - inputsMoved() returns true immediately without this!
|
||||
sleep_ms(30);
|
||||
#endif // defined(SIMU)
|
||||
|
||||
inactivityCheckInputs();
|
||||
splashStartTime += SPLASH_TIMEOUT;
|
||||
while (splashStartTime >= get_tmr10ms()) {
|
||||
LvglWrapper::instance()->run();
|
||||
|
||||
@@ -30,7 +30,7 @@ set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hal/key_driver.cpp
|
||||
)
|
||||
|
||||
if (NOT NATIVE_BUILD)
|
||||
if ((NOT NATIVE_BUILD) OR SIMU_DISKIO)
|
||||
set(SRC ${SRC}
|
||||
hal/storage.cpp
|
||||
hal/fatfs_diskio.cpp
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#if defined(STORAGE_USE_SDIO)
|
||||
#if defined(SIMU_DISKIO)
|
||||
extern const diskio_driver_t simu_diskio_driver;
|
||||
#define _STORAGE_DRIVER simu_diskio_driver
|
||||
#elif defined(STORAGE_USE_SDIO)
|
||||
#include "diskio_sdio.h"
|
||||
#define _STORAGE_DRIVER sdio_diskio_driver
|
||||
#elif defined(STORAGE_USE_SDCARD_SPI)
|
||||
|
||||
@@ -52,6 +52,11 @@ void inactivityTimerReset(ActivitySource src)
|
||||
#define INAC_STICKS_SHIFT 7
|
||||
#define INAC_SWITCHES_SHIFT 8
|
||||
|
||||
static inline int8_t inactivity_diff(uint8_t sum)
|
||||
{
|
||||
return abs((int8_t)(sum - inactivity.sum));
|
||||
}
|
||||
|
||||
bool inactivityCheckInputs()
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
@@ -78,7 +83,7 @@ bool inactivityCheckInputs()
|
||||
sum += get_spacemouse_value(i) >> INAC_STICKS_SHIFT;
|
||||
#endif
|
||||
|
||||
if (abs((int8_t)(sum - inactivity.sum)) > 1) {
|
||||
if (inactivity_diff(sum) > 1) {
|
||||
inactivity.sum = sum;
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -109,7 +109,7 @@ FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime)
|
||||
return res;
|
||||
}
|
||||
|
||||
#if !defined(BOOT) && !defined(SIMU)
|
||||
#if !defined(BOOT) && (!defined(SIMU) || defined(SIMU_DISKIO))
|
||||
|
||||
// Replace FatFS implementation of f_puts and f_printf
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static timer_queue* _instance = nullptr;
|
||||
static std::mutex _instance_mut;
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ void sdDone()
|
||||
|
||||
uint32_t sdMounted()
|
||||
{
|
||||
#if defined(SIMU)
|
||||
#if defined(SIMU) && !defined(SIMU_DISKIO)
|
||||
return true;
|
||||
#else
|
||||
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
|
||||
|
||||
@@ -1,727 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#if defined(SIMU_AUDIO)
|
||||
#include <SDL.h>
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
#include "fx.h"
|
||||
#include "FXExpression.h"
|
||||
#include "FXPNGImage.h"
|
||||
#include <unistd.h>
|
||||
#include "fxkeys.h"
|
||||
#include "edgetx.h"
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "targets/simu/simulcd.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/rotary_encoder.h"
|
||||
|
||||
#include "os/time.h"
|
||||
|
||||
#if LCD_W > 212
|
||||
#define LCD_ZOOM 1
|
||||
#else
|
||||
#define LCD_ZOOM 2
|
||||
#endif
|
||||
|
||||
#define W2 LCD_W*LCD_ZOOM
|
||||
#define H2 LCD_H*LCD_ZOOM
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
#define TAP_TIME 25
|
||||
|
||||
tmr10ms_t now = 0;
|
||||
tmr10ms_t downTime = 0;
|
||||
tmr10ms_t tapTime = 0;
|
||||
short tapCount = 0;
|
||||
#endif
|
||||
|
||||
class OpenTxSim: public FXMainWindow
|
||||
{
|
||||
FXDECLARE(OpenTxSim)
|
||||
|
||||
public:
|
||||
OpenTxSim(){};
|
||||
OpenTxSim(FXApp* a);
|
||||
~OpenTxSim();
|
||||
void updateKeysAndSwitches(bool start=false);
|
||||
long onKeypress(FXObject*, FXSelector, void*);
|
||||
long onMouseDown(FXObject*,FXSelector,void*);
|
||||
long onMouseUp(FXObject*,FXSelector,void*);
|
||||
long onMouseMove(FXObject*,FXSelector,void*);
|
||||
long onTimeout(FXObject*, FXSelector, void*);
|
||||
void createBitmap(int index, uint16_t *data, int x, int y, int w, int h);
|
||||
void makeSnapshot(const FXDrawable* drawable);
|
||||
void doEvents();
|
||||
void refreshDisplay();
|
||||
void setPixel(int x, int y, FXColor color);
|
||||
void fsLedRGB(uint8_t idx, uint32_t color);
|
||||
|
||||
private:
|
||||
FXImage * bmp;
|
||||
FXImageFrame * bmf;
|
||||
|
||||
public:
|
||||
FXSlider * sliders[MAX_STICKS];
|
||||
FXKnob * knobs[MAX_POTS];
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
FXButton * fctButtons[NUM_FUNCTIONS_SWITCHES] = {0};
|
||||
#endif
|
||||
};
|
||||
|
||||
// Message Map
|
||||
FXDEFMAP(OpenTxSim) OpenTxSimMap[] = {
|
||||
// Message_Type _______ID____Message_Handler_______
|
||||
FXMAPFUNC(SEL_TIMEOUT, 2, OpenTxSim::onTimeout),
|
||||
FXMAPFUNC(SEL_KEYPRESS, 0, OpenTxSim::onKeypress),
|
||||
FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, OpenTxSim::onMouseDown),
|
||||
FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, OpenTxSim::onMouseUp),
|
||||
FXMAPFUNC(SEL_MOTION, 0, OpenTxSim::onMouseMove),
|
||||
};
|
||||
|
||||
FXIMPLEMENT(OpenTxSim, FXMainWindow, OpenTxSimMap, ARRAYNUMBER(OpenTxSimMap))
|
||||
|
||||
OpenTxSim::OpenTxSim(FXApp* a):
|
||||
FXMainWindow(a, "EdgeTX Simu", nullptr, nullptr, DECOR_ALL, 20, 90, 0, 0)
|
||||
{
|
||||
bmp = new FXPPMImage(getApp(), nullptr, IMAGE_OWNED|IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP, W2, H2);
|
||||
|
||||
#if defined(SIMU_AUDIO)
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
putenv("SDL_AUDIODRIVER=directsound");
|
||||
#endif
|
||||
SDL_Init(SDL_INIT_AUDIO);
|
||||
#endif
|
||||
|
||||
FXHorizontalFrame * hf11 = new FXHorizontalFrame(this, LAYOUT_CENTER_X);
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
FXHorizontalFrame * hf12 = new FXHorizontalFrame(this, LAYOUT_CENTER_X);
|
||||
#endif
|
||||
FXHorizontalFrame * hf1 = new FXHorizontalFrame(this, LAYOUT_FILL_X);
|
||||
|
||||
//rh lv rv lh
|
||||
for (int i=0; i<4; i++) {
|
||||
switch (i) {
|
||||
#define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
|
||||
case 0:
|
||||
sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_HORIZONTAL, 10, 110, 100, 20);
|
||||
break;
|
||||
case 1:
|
||||
sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_VERTICAL, 110, 10, 20, 100);
|
||||
break;
|
||||
case 2:
|
||||
sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_VERTICAL, 130, 10, 20, 100);
|
||||
break;
|
||||
case 3:
|
||||
sliders[i]=new FXSlider(hf1, nullptr, 0, L|SLIDER_HORIZONTAL, 150, 110, 100, 20);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
sliders[i]->setTickDelta(14);
|
||||
sliders[i]->setRange(0, 4095);
|
||||
sliders[i]->setValue(2047);
|
||||
}
|
||||
|
||||
auto max_pots = adcGetMaxInputs(ADC_INPUT_FLEX);
|
||||
memset(knobs, 0, sizeof(knobs));
|
||||
|
||||
for (int i = 0; i < max_pots; i++) {
|
||||
knobs[i]= new FXKnob(hf11, nullptr, 0, KNOB_TICKS|LAYOUT_LEFT);
|
||||
knobs[i]->setRange(0, 4095);
|
||||
knobs[i]->setValue(2047);
|
||||
|
||||
#if defined(PCBHORUS) && !defined(FUNCTION_SWITCHES)
|
||||
if (i == 1) { // 6-pos switch
|
||||
knobs[i]->setIncrement(4095 / 5);
|
||||
knobs[i]->setTickDelta(4095 / 5);
|
||||
knobs[i]->setValue(0);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
for(int i = 0; i < NUM_FUNCTIONS_SWITCHES; ++i)
|
||||
{
|
||||
fctButtons[i] = new FXButton(hf12, " ", nullptr, nullptr, 123, LAYOUT_LEFT|BUTTON_NORMAL, 0, 0, 50, 0, 15, 15);
|
||||
fctButtons[i]->setBorderColor(FXColor(0));
|
||||
}
|
||||
#endif
|
||||
|
||||
bmf = new FXImageFrame(this, bmp);
|
||||
bmf->enable();
|
||||
bmf->setTarget(this);
|
||||
|
||||
updateKeysAndSwitches(true);
|
||||
|
||||
getApp()->addTimeout(this, 2, 100);
|
||||
}
|
||||
|
||||
OpenTxSim::~OpenTxSim()
|
||||
{
|
||||
TRACE("OpenTxSim::~OpenTxSim()");
|
||||
|
||||
simuStop();
|
||||
|
||||
delete bmp;
|
||||
delete sliders[0];
|
||||
delete sliders[1];
|
||||
delete sliders[2];
|
||||
delete sliders[3];
|
||||
|
||||
for (int i = 0; i < MAX_POTS; i++) {
|
||||
delete knobs[i];
|
||||
}
|
||||
|
||||
delete bmf;
|
||||
|
||||
#if defined(SIMU_AUDIO)
|
||||
SDL_Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenTxSim::createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
|
||||
{
|
||||
FXPNGImage snapshot(getApp(), nullptr, IMAGE_OWNED, w, h);
|
||||
|
||||
for (int i=0; i<w; i++) {
|
||||
for (int j=0; j<h; j++) {
|
||||
pixel_t z = data[(y+j) * LCD_W + (x+i)];
|
||||
FXColor color = FXRGB(255*((z&0xF00)>>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f);
|
||||
snapshot.setPixel(i, j, color);
|
||||
}
|
||||
}
|
||||
|
||||
FXFileStream stream;
|
||||
char buf[32];
|
||||
sprintf(buf, "%02d.png", index);
|
||||
if (stream.open(buf, FXStreamSave)) {
|
||||
snapshot.savePixels(stream);
|
||||
stream.close();
|
||||
TRACE("Bitmap %d (w=%d, h=%d) created", index, w, h);
|
||||
}
|
||||
else {
|
||||
TRACE("Bitmap %d (w=%d, h=%d) error", index, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTxSim::makeSnapshot(const FXDrawable* drawable)
|
||||
{
|
||||
// Construct and create an FXImage object
|
||||
FXPNGImage snapshot(getApp(), nullptr, 0, drawable->getWidth(), drawable->getHeight());
|
||||
snapshot.create();
|
||||
|
||||
// Create a window device context and lock it onto the image
|
||||
FXDCWindow dc(&snapshot);
|
||||
|
||||
// Draw from the widget to this
|
||||
dc.drawArea(drawable, 0, 0, drawable->getWidth(), drawable->getHeight(), 0, 0);
|
||||
|
||||
// Release lock
|
||||
dc.end();
|
||||
|
||||
// Grab pixels from server side back to client side
|
||||
snapshot.restore();
|
||||
|
||||
// Save recovered pixels to a file
|
||||
FXFileStream stream;
|
||||
char buf[100];
|
||||
|
||||
do {
|
||||
stream.close();
|
||||
sprintf(buf, "snapshot_%02d.png", ++g_snapshot_idx);
|
||||
} while (stream.open(buf, FXStreamLoad));
|
||||
|
||||
if (stream.open(buf, FXStreamSave)) {
|
||||
snapshot.savePixels(stream);
|
||||
stream.close();
|
||||
printf("Snapshot written: %s\n", buf);
|
||||
}
|
||||
else {
|
||||
printf("Cannot create snapshot %s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTxSim::doEvents()
|
||||
{
|
||||
getApp()->runOneEvent(false);
|
||||
}
|
||||
|
||||
long OpenTxSim::onKeypress(FXObject *, FXSelector, void * v)
|
||||
{
|
||||
auto * evt = (FXEvent *)v;
|
||||
|
||||
// TRACE("keypress %x", evt->code);
|
||||
|
||||
if (evt->code == 's') {
|
||||
makeSnapshot(bmf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long OpenTxSim::onMouseDown(FXObject *, FXSelector, void * v)
|
||||
{
|
||||
FXEvent * evt = (FXEvent *)v;
|
||||
UNUSED(evt);
|
||||
|
||||
TRACE_WINDOWS("[Mouse Press] %d %d", evt->win_x, evt->win_y);
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
now = get_tmr10ms();
|
||||
simTouchState.tapCount = 0;
|
||||
|
||||
simTouchState.event = TE_DOWN;
|
||||
simTouchState.startX = simTouchState.x = evt->win_x;
|
||||
simTouchState.startY = simTouchState.y = evt->win_y;
|
||||
downTime = now;
|
||||
simTouchOccured = true;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long OpenTxSim::onMouseUp(FXObject*,FXSelector,void*v)
|
||||
{
|
||||
FXEvent * evt = (FXEvent *)v;
|
||||
UNUSED(evt);
|
||||
|
||||
TRACE_WINDOWS("[Mouse Release] %d %d", evt->win_x, evt->win_y);
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
now = get_tmr10ms();
|
||||
|
||||
if (simTouchState.event == TE_DOWN) {
|
||||
simTouchState.event = TE_UP;
|
||||
simTouchState.x = simTouchState.startX;
|
||||
simTouchState.y = simTouchState.startY;
|
||||
if (now - downTime <= TAP_TIME) {
|
||||
if (now - tapTime > TAP_TIME)
|
||||
tapCount = 1;
|
||||
else
|
||||
tapCount++;
|
||||
simTouchState.tapCount = tapCount;
|
||||
tapTime = now;
|
||||
}
|
||||
}
|
||||
else {
|
||||
simTouchState.event = TE_SLIDE_END;
|
||||
}
|
||||
simTouchOccured = true;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v)
|
||||
{
|
||||
FXEvent * evt = (FXEvent *)v;
|
||||
UNUSED(evt);
|
||||
|
||||
if (evt->state & LEFTBUTTONMASK) {
|
||||
TRACE_WINDOWS("[Mouse Move] %d %d", evt->win_x, evt->win_y);
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
simTouchState.deltaX += evt->win_x - simTouchState.x;
|
||||
simTouchState.deltaY += evt->win_y - simTouchState.y;
|
||||
if (simTouchState.event == TE_SLIDE || abs(simTouchState.deltaX) >= SLIDE_RANGE || abs(simTouchState.deltaY) >= SLIDE_RANGE) {
|
||||
simTouchState.event = TE_SLIDE;
|
||||
simTouchState.x = evt->win_x;
|
||||
simTouchState.y = evt->win_y;
|
||||
}
|
||||
simTouchOccured = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OpenTxSim::updateKeysAndSwitches(bool start)
|
||||
{
|
||||
static int keys[] = {
|
||||
#if defined(PCBFLYSKY)
|
||||
// no keys
|
||||
#elif defined(PCBHORUS)
|
||||
KEY_Page_Up, KEY_PAGEUP,
|
||||
KEY_Page_Down, KEY_PAGEDN,
|
||||
KEY_Return, KEY_ENTER,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Up, KEY_UP,
|
||||
KEY_Down, KEY_DOWN,
|
||||
KEY_Right, KEY_RIGHT,
|
||||
KEY_Left, KEY_LEFT,
|
||||
#elif defined(PCBXLITE) || defined(RADIO_FAMILY_JUMPER_T12)
|
||||
#if defined(KEYS_GPIO_REG_SHIFT)
|
||||
KEY_Shift_L, KEY_SHIFT,
|
||||
#endif
|
||||
KEY_Return, KEY_ENTER,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Right, KEY_RIGHT,
|
||||
KEY_Left, KEY_LEFT,
|
||||
KEY_Up, KEY_UP,
|
||||
KEY_Down, KEY_DOWN,
|
||||
#elif defined(NAVIGATION_X7_TX12)
|
||||
KEY_Page_Up, KEY_PAGEUP,
|
||||
KEY_Page_Down, KEY_PAGEDN,
|
||||
KEY_Return, KEY_ENTER,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Up, KEY_MODEL,
|
||||
KEY_Down, KEY_EXIT,
|
||||
KEY_Right, KEY_TELE,
|
||||
KEY_Left, KEY_SYS,
|
||||
#elif defined(RADIO_T8) || defined(RADIO_COMMANDO8)
|
||||
KEY_Page_Up, KEY_PAGEUP,
|
||||
KEY_Page_Down, KEY_PAGEDN,
|
||||
KEY_Return, KEY_ENTER,
|
||||
KEY_Right, KEY_MODEL,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Left, KEY_SYS,
|
||||
KEY_Up, KEY_PLUS,
|
||||
KEY_Down, KEY_MINUS,
|
||||
#elif defined(PCBTARANIS)
|
||||
KEY_Page_Up, KEY_MENU,
|
||||
KEY_Page_Down, KEY_PAGEDN,
|
||||
KEY_Return, KEY_ENTER,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Up, KEY_PLUS,
|
||||
KEY_Down, KEY_MINUS,
|
||||
#else
|
||||
KEY_Return, KEY_MENU,
|
||||
KEY_BackSpace, KEY_EXIT,
|
||||
KEY_Right, KEY_RIGHT,
|
||||
KEY_Left, KEY_LEFT,
|
||||
KEY_Up, KEY_UP,
|
||||
KEY_Down, KEY_DOWN,
|
||||
#endif
|
||||
};
|
||||
|
||||
for (unsigned int i=0; i<DIM(keys); i+=2) {
|
||||
simuSetKey(keys[i+1], start ? false : getApp()->getKeyState(keys[i]));
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static FXuint trimKeys[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0 };
|
||||
#else
|
||||
static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12 };
|
||||
#endif
|
||||
|
||||
for (unsigned i = 0; i < sizeof(trimKeys) / sizeof(FXuint); i++) {
|
||||
simuSetTrim(i, getApp()->getKeyState(trimKeys[i]));
|
||||
}
|
||||
|
||||
#define SWITCH_KEY(key, swtch, states) \
|
||||
static bool state##key = 0; \
|
||||
static int8_t state_##swtch = -1; \
|
||||
static int8_t inc_##swtch = 4-states; \
|
||||
if (getApp()->getKeyState(KEY_##key)) { \
|
||||
if (!state##key) { \
|
||||
state_##swtch = (state_##swtch+inc_##swtch); \
|
||||
if (state_##swtch == -1+((states-1)*inc_##swtch)) inc_##swtch = -4+states; \
|
||||
else if (state_##swtch == -1) inc_##swtch = 4-states; \
|
||||
state##key = true; \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
state##key = false; \
|
||||
} \
|
||||
simuSetSwitch(swtch, state_##swtch) \
|
||||
|
||||
SWITCH_KEY(A, 0, 3);
|
||||
SWITCH_KEY(B, 1, 3);
|
||||
SWITCH_KEY(C, 2, 3);
|
||||
SWITCH_KEY(D, 3, 3);
|
||||
|
||||
#if defined(RADIO_TPRO) || defined(RADIO_TPROV2) || defined(RADIO_TPROS) || defined(RADIO_BUMBLEBEE)
|
||||
SWITCH_KEY(1, 4, 2);
|
||||
SWITCH_KEY(2, 5, 2);
|
||||
SWITCH_KEY(3, 6, 2);
|
||||
SWITCH_KEY(4, 7, 2);
|
||||
SWITCH_KEY(5, 8, 2);
|
||||
SWITCH_KEY(6, 9, 2);
|
||||
#elif defined(HARDWARE_SWITCH_G) && defined(HARDWARE_SWITCH_H)
|
||||
SWITCH_KEY(E, 4, 3);
|
||||
SWITCH_KEY(F, 5, 2);
|
||||
SWITCH_KEY(G, 6, 3);
|
||||
SWITCH_KEY(H, 7, 2);
|
||||
#elif defined(HARDWARE_SWITCH_F) && defined(HARDWARE_SWITCH_H)
|
||||
SWITCH_KEY(F, 4, 2);
|
||||
SWITCH_KEY(H, 5, 2);
|
||||
#endif
|
||||
|
||||
#if defined(PCBX9E)
|
||||
SWITCH_KEY(I, 8, 3);
|
||||
SWITCH_KEY(J, 9, 3);
|
||||
SWITCH_KEY(K, 10, 3);
|
||||
SWITCH_KEY(L, 11, 3);
|
||||
SWITCH_KEY(M, 12, 3);
|
||||
SWITCH_KEY(N, 13, 3);
|
||||
SWITCH_KEY(O, 14, 3);
|
||||
SWITCH_KEY(P, 15, 3);
|
||||
SWITCH_KEY(Q, 16, 3);
|
||||
SWITCH_KEY(R, 17, 3);
|
||||
#endif
|
||||
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
for(int i = 0; i < NUM_FUNCTIONS_SWITCHES; ++i)
|
||||
{
|
||||
int8_t newState = -1;
|
||||
if(fctButtons[i]->getState()==STATE_DOWN)
|
||||
{
|
||||
newState = 1;
|
||||
}
|
||||
|
||||
simuSetSwitch(switchGetMaxSwitches() + i, newState);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern volatile rotenc_t rotencValue;
|
||||
extern volatile uint32_t rotencDt;
|
||||
|
||||
long OpenTxSim::onTimeout(FXObject*, FXSelector, void*)
|
||||
{
|
||||
if (hasFocus()) {
|
||||
|
||||
updateKeysAndSwitches();
|
||||
|
||||
#if defined(ROTARY_ENCODER_NAVIGATION)
|
||||
static bool rotencAction = false;
|
||||
if (getApp()->getKeyState(KEY_X) || getApp()->getKeyState(KEY_plus)) {
|
||||
if (!rotencAction) rotencValue += ROTARY_ENCODER_GRANULARITY;
|
||||
rotencAction = true;
|
||||
}
|
||||
else if (getApp()->getKeyState(KEY_W) || getApp()->getKeyState(KEY_minus)) {
|
||||
if (!rotencAction) rotencValue -= ROTARY_ENCODER_GRANULARITY;
|
||||
rotencAction = true;
|
||||
}
|
||||
else {
|
||||
rotencAction = false;
|
||||
}
|
||||
|
||||
static uint32_t last_tick = 0;
|
||||
if (rotencAction) {
|
||||
uint32_t now = time_get_ms();
|
||||
uint32_t dt = now - last_tick;
|
||||
rotencDt += dt;
|
||||
last_tick = now;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SIMU_BOOTLOADER)
|
||||
void interrupt10ms();
|
||||
interrupt10ms();
|
||||
#endif
|
||||
|
||||
refreshDisplay();
|
||||
getApp()->addTimeout(this, 2, 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LCD_W >= 212
|
||||
#define BL_COLOR FXRGB(47, 123, 227)
|
||||
#else
|
||||
#define BL_COLOR FXRGB(150, 200, 152)
|
||||
#endif
|
||||
|
||||
void OpenTxSim::setPixel(int x, int y, FXColor color)
|
||||
{
|
||||
#if LCD_ZOOM > 1
|
||||
for (int i=0; i<LCD_ZOOM; ++i) {
|
||||
for (int j=0; j<LCD_ZOOM; ++j) {
|
||||
bmp->setPixel(LCD_ZOOM*x+i, LCD_ZOOM*y+j, color);
|
||||
}
|
||||
}
|
||||
#else
|
||||
bmp->setPixel(x, y, color);
|
||||
#endif
|
||||
}
|
||||
|
||||
// from lcd driver
|
||||
void lcdFlushed();
|
||||
|
||||
void OpenTxSim::refreshDisplay()
|
||||
{
|
||||
static bool lightEnabled = (bool)isBacklightEnabled();
|
||||
|
||||
if ((bool(isBacklightEnabled()) != lightEnabled) || simuLcdRefresh) {
|
||||
|
||||
if (bool(isBacklightEnabled()) != lightEnabled) {
|
||||
lightEnabled = (bool)isBacklightEnabled();
|
||||
TRACE("backlight %s", lightEnabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
#if !defined(COLORLCD)
|
||||
FXColor offColor = isBacklightEnabled() ? BL_COLOR : FXRGB(200, 200, 200);
|
||||
#if LCD_DEPTH == 1
|
||||
FXColor onColor = FXRGB(0, 0, 0);
|
||||
#endif
|
||||
#endif
|
||||
for (int x = 0; x < LCD_W; x++) {
|
||||
for (int y = 0; y < LCD_H; y++) {
|
||||
#if defined(COLORLCD)
|
||||
pixel_t z = simuLcdBuf[y * LCD_W + x];
|
||||
FXColor color =
|
||||
FXRGB(((z & 0xF800) >> 8) + ((z & 0xE000) >> 13),
|
||||
((z & 0x07E0) >> 3) + ((z & 0x0600) >> 9),
|
||||
(((z & 0x001F) << 3) & 0x00F8) + ((z & 0x001C) >> 2));
|
||||
setPixel(x, y, color);
|
||||
#else
|
||||
#if LCD_DEPTH == 4
|
||||
pixel_t *p = &simuLcdBuf[y / 2 * LCD_W + x];
|
||||
uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F);
|
||||
if (z) {
|
||||
FXColor color;
|
||||
if (isBacklightEnabled())
|
||||
color = FXRGB(47 - (z * 47) / 15, 123 - (z * 123) / 15,
|
||||
227 - (z * 227) / 15);
|
||||
else
|
||||
color = FXRGB(200 - (z * 200) / 15, 200 - (z * 200) / 15,
|
||||
200 - (z * 200) / 15);
|
||||
setPixel(x, y, color);
|
||||
}
|
||||
#else // LCD_DEPTH == 1
|
||||
if (simuLcdBuf[x + (y / 8) * LCD_W] & (1 << (y % 8))) {
|
||||
setPixel(x, y, onColor);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
setPixel(x, y, offColor);
|
||||
}
|
||||
#endif // !defined(COLORLCD)
|
||||
}
|
||||
}
|
||||
#if defined(COLORLCD)
|
||||
if (!lightEnabled) { bmp->fade(0, 50); }
|
||||
#endif
|
||||
|
||||
bmp->render();
|
||||
bmf->setImage(bmp);
|
||||
|
||||
simuLcdRefresh = false;
|
||||
lcdFlushed();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTxSim::fsLedRGB(uint8_t idx, uint32_t color)
|
||||
{
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
uint32_t col = (color&0x00FF0000)>>16 | (color&0x0000FF00) | (color&0x000000FF)<<16;
|
||||
fctButtons[idx]->setBaseColor(col);
|
||||
fctButtons[idx]->setBackColor(col);
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenTxSim * opentxSim;
|
||||
|
||||
void doFxEvents()
|
||||
{
|
||||
//puts("doFxEvents");
|
||||
opentxSim->getApp()->runOneEvent(false);
|
||||
opentxSim->refreshDisplay();
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
// Each FOX GUI program needs one, and only one, application object.
|
||||
// The application objects coordinates some common stuff shared between
|
||||
// all the widgets; for example, it dispatches events, keeps track of
|
||||
// all the windows, and so on.
|
||||
// We pass the "name" of the application, and its "vendor", the name
|
||||
// and vendor are used to search the registry database (which stores
|
||||
// persistent information e.g. fonts and colors).
|
||||
FXApp application("OpenTX Simu", "OpenTX");
|
||||
|
||||
// Here we initialize the application. We pass the command line arguments
|
||||
// because FOX may sometimes need to filter out some of the arguments.
|
||||
// This opens up the display as well, and reads the registry database
|
||||
// so that persistent settings are now available.
|
||||
application.init(argc, argv);
|
||||
|
||||
simuInit();
|
||||
|
||||
// This creates the main window. We pass in the title to be displayed
|
||||
// above the window, and possibly some icons for when its iconified.
|
||||
// The decorations determine stuff like the borders, close buttons,
|
||||
// drag handles, and so on the Window Manager is supposed to give this
|
||||
// window.
|
||||
//FXMainWindow *main=new FXMainWindow(&application, "Hello", nullptr, nullptr, DECOR_ALL);
|
||||
opentxSim = new OpenTxSim(&application);
|
||||
application.create();
|
||||
|
||||
// Pretty self-explanatory:- this shows the window, and places it in the
|
||||
// middle of the screen.
|
||||
#ifndef __APPLE__
|
||||
opentxSim->show(PLACEMENT_SCREEN);
|
||||
#else
|
||||
opentxSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
|
||||
#endif
|
||||
|
||||
printf("Model size = %d\n", (int)sizeof(g_model));
|
||||
|
||||
#if !defined(SIMU_BOOTLOADER)
|
||||
startAudio();
|
||||
#endif
|
||||
|
||||
simuStart(true/*false*/, argc >= 3 ? argv[2] : 0, argc >= 4 ? argv[3] : 0);
|
||||
|
||||
return application.run();
|
||||
}
|
||||
|
||||
uint16_t simu_get_analog(uint8_t idx)
|
||||
{
|
||||
auto max_sticks = adcGetMaxInputs(ADC_INPUT_MAIN);
|
||||
if (idx < max_sticks)
|
||||
return opentxSim->sliders[idx]->getValue();
|
||||
|
||||
idx -= max_sticks;
|
||||
|
||||
auto max_pots = adcGetMaxInputs(ADC_INPUT_FLEX);
|
||||
if (idx < max_pots)
|
||||
return opentxSim->knobs[idx]->getValue();
|
||||
|
||||
// probably RTC_BAT
|
||||
return 0;
|
||||
}
|
||||
|
||||
void createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
|
||||
{
|
||||
opentxSim->createBitmap(index, data, x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
void fsLedRGB(uint8_t idx, uint32_t color)
|
||||
{
|
||||
opentxSim->fsLedRGB(idx, color);
|
||||
}
|
||||
|
||||
void fsLedOn(uint8_t idx)
|
||||
{
|
||||
opentxSim->fsLedRGB(idx, 0x00ffffff);
|
||||
}
|
||||
|
||||
void fsLedOff(uint8_t idx)
|
||||
{
|
||||
opentxSim->fsLedRGB(idx, 0);
|
||||
}
|
||||
@@ -21,8 +21,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "edgetx_types.h"
|
||||
#include "edgetx_constants.h"
|
||||
|
||||
enum LogicalSwitchFamilies {
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
|
||||
option(SIMU_TARGET "Configure libsimulator/simu targets (can be turned off for compiling Companion only)" ON)
|
||||
option(SIMU_TARGET "Configure libsimulator/simu targets" ON)
|
||||
if(NOT SIMU_TARGET)
|
||||
message(STATUS "libsimulator/simu targets disabled")
|
||||
return()
|
||||
endif()
|
||||
|
||||
target_compile_options(radiolib_native PUBLIC -DSIMU)
|
||||
|
||||
if(SDL2_FOUND)
|
||||
option(SIMU_AUDIO "Enable simulator audio." ON)
|
||||
if(SIMU_AUDIO)
|
||||
target_compile_options(radiolib_native PUBLIC -DSIMU_AUDIO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SIMU_DRIVERS
|
||||
simpgmspace.cpp
|
||||
simufatfs.cpp
|
||||
simudisk.cpp
|
||||
simulcd.cpp
|
||||
simuaudio.cpp
|
||||
switch_driver.cpp
|
||||
adc_driver.cpp
|
||||
module_drivers.cpp
|
||||
@@ -156,60 +166,44 @@ if(WIN32)
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "/EHsc")
|
||||
if(NOT CLANG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /LD /MP")
|
||||
endif()
|
||||
else()
|
||||
if(MINGW)
|
||||
# struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
|
||||
if(ASAN)
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
|
||||
endif()
|
||||
if(MINGW)
|
||||
# struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
|
||||
|
||||
if(ASAN)
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
|
||||
add_executable(simu
|
||||
EXCLUDE_FROM_ALL
|
||||
${SIMU_SRC}
|
||||
display.cpp
|
||||
widgets.cpp
|
||||
knobs.cpp
|
||||
sdl_simu.cpp
|
||||
)
|
||||
|
||||
if(FOX_FOUND)
|
||||
add_executable(simu WIN32
|
||||
EXCLUDE_FROM_ALL
|
||||
${SIMU_SRC}
|
||||
${RADIO_SRC_DIR}/simu.cpp)
|
||||
include(FetchImgui)
|
||||
target_compile_options(simu PUBLIC -DSIMU)
|
||||
|
||||
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} )
|
||||
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL2_LIBRARIES})
|
||||
target_compile_options(simu PRIVATE ${SIMU_SRC_OPTIONS})
|
||||
endif()
|
||||
add_png_target(image_assets "assets/images/*.png")
|
||||
add_dependencies(simu image_assets)
|
||||
|
||||
if(APPLE)
|
||||
# OS X compiler no longer automatically includes /Library/Frameworks in search path
|
||||
set(CMAKE_SHARED_LINKER_FLAGS -F/Library/Frameworks)
|
||||
set(STB_DIR ${RADIO_SRC_DIR}/thirdparty/stb)
|
||||
target_include_directories(simu PUBLIC ${STB_DIR})
|
||||
target_link_libraries(simu PUBLIC imgui)
|
||||
|
||||
set(SIMULATOR_BUNDLES)
|
||||
foreach(library ${OPENTX_LIBRARIES})
|
||||
set(SIMULATOR_BUNDLE "${library}-bundle")
|
||||
add_custom_target(${SIMULATOR_BUNDLE}
|
||||
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore lib${library}.dylib
|
||||
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork lib${library}.dylib
|
||||
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtXml.framework/Versions/4/QtXml @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml lib${library}.dylib
|
||||
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui lib${library}.dylib
|
||||
COMMAND install_name_tool -change @rpath/SDL.framework/Versions/A/SDL @executable_path/../Frameworks/SDL.framework/Versions/A/SDL lib${library}.dylib
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMAND pwd
|
||||
COMMAND cp lib${library}.dylib companion.app/Contents/Resources/
|
||||
DEPENDS ${library}
|
||||
)
|
||||
list(APPEND SIMULATOR_BUNDLES ${SIMULATOR_BUNDLE})
|
||||
endforeach()
|
||||
add_custom_target(opentx-simulators-bundle DEPENDS ${SIMULATOR_BUNDLES})
|
||||
endif(APPLE)
|
||||
target_link_libraries(simu PUBLIC
|
||||
pthread
|
||||
${SDL2_LIBRARIES}
|
||||
)
|
||||
|
||||
PrintTargetReport("simu/libsimulator")
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -19,11 +19,35 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
|
||||
bool boardBacklightOn = false;
|
||||
bool isBacklightEnabled() { return boardBacklightOn; }
|
||||
void backlightFullOn() { boardBacklightOn = true; }
|
||||
|
||||
void backlightInit() {}
|
||||
void backlightEnable(unsigned char) {}
|
||||
void backlightEnable(unsigned char, unsigned char) {}
|
||||
void backlightDisable() {}
|
||||
|
||||
#if !defined(COLORLCD)
|
||||
|
||||
void backlightFullOn() { boardBacklightOn = true; }
|
||||
|
||||
void backlightEnable(unsigned char)
|
||||
{
|
||||
boardBacklightOn = true;
|
||||
}
|
||||
|
||||
void backlightEnable(unsigned char, unsigned char)
|
||||
{
|
||||
boardBacklightOn = true;
|
||||
}
|
||||
|
||||
void backlightDisable()
|
||||
{
|
||||
boardBacklightOn = false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void backlightFullOn() { backlightEnable(BACKLIGHT_LEVEL_MAX); }
|
||||
void backlightEnable(uint8_t) {}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "display.h"
|
||||
#include "simu.h"
|
||||
|
||||
static void _set_pixel(uint8_t* pixel, const SDL_Color& color)
|
||||
{
|
||||
pixel[0] = color.a;
|
||||
pixel[1] = color.b;
|
||||
pixel[2] = color.g;
|
||||
pixel[3] = color.r;
|
||||
}
|
||||
|
||||
static void _blit_simu_screen_color(void* screen_buffer, Uint32 format, int w, int h, int pitch)
|
||||
{
|
||||
pixel_t* src_buffer = simuLcdBuf;
|
||||
uint8_t* line_buffer = (uint8_t*)screen_buffer;
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
pixel_t z = *src_buffer++;
|
||||
// Alpha
|
||||
line_buffer[0] = SDL_ALPHA_OPAQUE;
|
||||
// Blue
|
||||
line_buffer[1] = (((z & 0x001F) << 3) & 0x00F8) + ((z & 0x001C) >> 2);
|
||||
// Green
|
||||
line_buffer[2] = ((z & 0x07E0) >> 3) + ((z & 0x0600) >> 9);
|
||||
// Red
|
||||
line_buffer[3] = ((z & 0xF800) >> 8) + ((z & 0xE000) >> 13);
|
||||
line_buffer += SDL_BYTESPERPIXEL(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _blit_simu_screen_1bit(void* screen_buffer, Uint32 format, int w, int h, int pitch)
|
||||
{
|
||||
const SDL_Color on_color = {.r = 0, .g = 0, .b = 0};
|
||||
|
||||
uint8_t* line_buffer = (uint8_t*)screen_buffer;
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
SDL_Color c = on_color;
|
||||
if (simuLcdBuf[x + (y / 8) * w] & (1 << (y % 8))) {
|
||||
c.a = SDL_ALPHA_OPAQUE;
|
||||
} else {
|
||||
c.a = SDL_ALPHA_TRANSPARENT;
|
||||
}
|
||||
_set_pixel(line_buffer, c);
|
||||
line_buffer += SDL_BYTESPERPIXEL(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static inline uint8_t _4bit_blend(uint16_t px, uint16_t bg)
|
||||
// {
|
||||
// uint16_t c = bg * (16 - px);
|
||||
// return c / 16;
|
||||
// }
|
||||
|
||||
static void _blit_simu_screen_4bit(void* screen_buffer, Uint32 format, int w, int h, int pitch)
|
||||
{
|
||||
const SDL_Color on_color = {.r = 0, .g = 0, .b = 0};
|
||||
|
||||
uint8_t* line_buffer = (uint8_t*)screen_buffer;
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
|
||||
pixel_t p = simuLcdBuf[y / 2 * LCD_W + x];
|
||||
uint8_t z = (y & 1) ? (p >> 4) : (p & 0x0F);
|
||||
|
||||
SDL_Color c = on_color;
|
||||
c.a = (uint16_t)z * SDL_ALPHA_OPAQUE / 16;
|
||||
|
||||
_set_pixel(line_buffer, c);
|
||||
line_buffer += SDL_BYTESPERPIXEL(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void refreshDisplay(SDL_Texture* screen)
|
||||
{
|
||||
if (simuLcdRefresh) {
|
||||
|
||||
// fetch texture format
|
||||
Uint32 format = 0;
|
||||
int width = 0, height = 0;
|
||||
|
||||
if (SDL_QueryTexture(screen, &format, nullptr, &width, &height) != 0) {
|
||||
SDL_Log("SDL_QueryTexture: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
// raw pixel buffer
|
||||
void* screen_buffer = nullptr;
|
||||
|
||||
// length of one row in bytes
|
||||
int pitch = 0;
|
||||
|
||||
if (SDL_LockTexture(screen, nullptr, &screen_buffer, &pitch) != 0) {
|
||||
SDL_Log("SDL_LockTexture: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (LCD_DEPTH == 1) {
|
||||
_blit_simu_screen_1bit(screen_buffer, format, width, height, pitch);
|
||||
} else if (LCD_DEPTH == 4) {
|
||||
_blit_simu_screen_4bit(screen_buffer, format, width, height, pitch);
|
||||
} else if (LCD_DEPTH == 16) {
|
||||
_blit_simu_screen_color(screen_buffer, format, width, height, pitch);
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(screen);
|
||||
simuLcdRefresh = false;
|
||||
lcdFlushed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
// display methods
|
||||
void refreshDisplay(SDL_Texture* screen);
|
||||
@@ -19,6 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
void gyroInit() {}
|
||||
void gyroRead() {}
|
||||
void gyroRead(unsigned char*) {}
|
||||
#include "gyro.h"
|
||||
|
||||
int gyroInit() { return -1; }
|
||||
int gyroRead(uint8_t*) { return -1; }
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
//
|
||||
// Code from:
|
||||
// https://github.com/altschuler/imgui-knobs
|
||||
//
|
||||
// Copyright (c) 2022 Simon Altschuler
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include "knobs.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#define IMGUIKNOBS_PI 3.14159265358979323846f
|
||||
|
||||
namespace ImGuiKnobs
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename DataType>
|
||||
struct knob {
|
||||
float radius;
|
||||
bool value_changed;
|
||||
ImVec2 center;
|
||||
bool is_active;
|
||||
bool is_hovered;
|
||||
float angle_min;
|
||||
float angle_max;
|
||||
float t;
|
||||
float angle;
|
||||
float angle_cos;
|
||||
float angle_sin;
|
||||
|
||||
knob(const char *_label, ImGuiDataType data_type, DataType *p_value,
|
||||
DataType v_min, DataType v_max, float _radius,
|
||||
const char *format, ImGuiKnobFlags flags)
|
||||
{
|
||||
radius = _radius;
|
||||
t = ((float)*p_value - v_min) / (v_max - v_min);
|
||||
auto screen_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
ImGui::InvisibleButton(_label, {radius * 2.0f, radius * 2.0f});
|
||||
|
||||
angle_min = IMGUIKNOBS_PI * 0.75f;
|
||||
angle_max = IMGUIKNOBS_PI * 2.25f;
|
||||
center = {screen_pos.x + radius, screen_pos.y + radius};
|
||||
is_active = ImGui::IsItemActive();
|
||||
is_hovered = ImGui::IsItemHovered();
|
||||
angle = angle_min + (angle_max - angle_min) * t;
|
||||
angle_cos = cosf(angle);
|
||||
angle_sin = sinf(angle);
|
||||
|
||||
if (is_active) {
|
||||
ImVec2 mp = ImGui::GetIO().MousePos;
|
||||
float angle_mid = (angle_max + angle_min) / 2.0;
|
||||
float alpha = atan2f(mp.x - center.x, center.y - mp.y) + angle_mid;
|
||||
alpha = ImClamp(alpha, angle_min, angle_max);
|
||||
float ratio = (alpha - angle_min) / (angle_max - angle_min);
|
||||
switch(data_type){
|
||||
case ImGuiDataType_Float:
|
||||
*p_value = ImGui::ScaleValueFromRatioT<ImS32, ImS32, float>(
|
||||
data_type, ratio, v_min, v_max, false, 0.0f, 0.0f);
|
||||
break;
|
||||
case ImGuiDataType_S32:
|
||||
*p_value = ImGui::ScaleValueFromRatioT<float, float, float>(
|
||||
data_type, ratio, v_min, v_max, false, 0.0f, 0.0f);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_dot(float size, float radius, float angle, color_set color,
|
||||
bool filled, int segments)
|
||||
{
|
||||
auto dot_size = size * this->radius;
|
||||
auto dot_radius = radius * this->radius;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(
|
||||
{center[0] + cosf(angle) * dot_radius,
|
||||
center[1] + sinf(angle) * dot_radius},
|
||||
dot_size,
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base),
|
||||
segments);
|
||||
}
|
||||
|
||||
void draw_tick(float start, float end, float width, float angle,
|
||||
color_set color)
|
||||
{
|
||||
auto tick_start = start * radius;
|
||||
auto tick_end = end * radius;
|
||||
auto angle_cos = cosf(angle);
|
||||
auto angle_sin = sinf(angle);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{center[0] + angle_cos * tick_end, center[1] + angle_sin * tick_end},
|
||||
{center[0] + angle_cos * tick_start,
|
||||
center[1] + angle_sin * tick_start},
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base),
|
||||
width * radius);
|
||||
}
|
||||
|
||||
void draw_circle(float size, color_set color, bool filled, int segments)
|
||||
{
|
||||
auto circle_radius = size * radius;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddCircleFilled(
|
||||
center, circle_radius,
|
||||
is_active ? color.active : (is_hovered ? color.hovered : color.base));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DataType>
|
||||
knob<DataType> knob_with_title(const char *label, ImGuiDataType data_type,
|
||||
DataType *p_value, DataType v_min, DataType v_max,
|
||||
const char *format, float size,
|
||||
ImGuiKnobFlags flags)
|
||||
{
|
||||
ImGui::PushID(label);
|
||||
auto width = size == 0 ? ImGui::GetTextLineHeight() * 4.0f
|
||||
: size * ImGui::GetIO().FontGlobalScale;
|
||||
ImGui::PushItemWidth(width);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// There's an issue with `SameLine` and Groups, see
|
||||
// https://github.com/ocornut/imgui/issues/4190. This is probably not the best
|
||||
// solution, but seems to work for now
|
||||
ImGui::GetCurrentWindow()->DC.CurrLineTextBaseOffset = 0;
|
||||
|
||||
// Draw title
|
||||
if (!(flags & ImGuiKnobFlags_NoTitle)) {
|
||||
auto title_size = ImGui::CalcTextSize(label, NULL, false, width);
|
||||
|
||||
// Center title
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() +
|
||||
(width - title_size[0]) * 0.5f);
|
||||
|
||||
ImGui::Text("%s", label);
|
||||
}
|
||||
|
||||
// Draw knob
|
||||
knob<DataType> k(label, data_type, p_value, v_min, v_max, width * 0.5f,
|
||||
format, flags);
|
||||
|
||||
// Draw tooltip
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) ||
|
||||
ImGui::IsItemActive()) {
|
||||
|
||||
if (flags & ImGuiKnobFlags_ValueTooltip && ImGui::IsItemActive()) {
|
||||
ImGui::SetTooltip(format, *p_value);
|
||||
} else if (flags & ImGuiKnobFlags_TitleTooltip) {
|
||||
ImGui::SetTooltip("%s", label);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PopID();
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
color_set GetPrimaryColorSet()
|
||||
{
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
|
||||
return {colors[ImGuiCol_ButtonActive], colors[ImGuiCol_ButtonHovered],
|
||||
colors[ImGuiCol_ButtonHovered]};
|
||||
}
|
||||
|
||||
color_set GetSecondaryColorSet()
|
||||
{
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
auto active = ImVec4(colors[ImGuiCol_ButtonActive].x * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].y * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].z * 0.5f,
|
||||
colors[ImGuiCol_ButtonActive].w);
|
||||
|
||||
auto hovered = ImVec4(colors[ImGuiCol_ButtonHovered].x * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].y * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].z * 0.5f,
|
||||
colors[ImGuiCol_ButtonHovered].w);
|
||||
|
||||
return {active, hovered, hovered};
|
||||
}
|
||||
|
||||
color_set GetTrackColorSet()
|
||||
{
|
||||
auto *colors = ImGui::GetStyle().Colors;
|
||||
|
||||
return {colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBg],
|
||||
colors[ImGuiCol_FrameBg]};
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename DataType>
|
||||
bool BaseKnob(const char *label, ImGuiDataType data_type, DataType *p_value,
|
||||
DataType v_min, DataType v_max, const char *format,
|
||||
ImGuiKnobVariant variant, float size, ImGuiKnobFlags flags,
|
||||
int steps = 10)
|
||||
{
|
||||
auto knob = detail::knob_with_title(label, data_type, p_value, v_min, v_max,
|
||||
format, size, flags);
|
||||
|
||||
switch (variant) {
|
||||
case ImGuiKnobVariant_Tick: {
|
||||
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_tick(0.5f, 0.85f, 0.1f, knob.angle,
|
||||
detail::GetPrimaryColorSet());
|
||||
break;
|
||||
}
|
||||
case ImGuiKnobVariant_Dot: {
|
||||
knob.draw_circle(0.85f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_dot(0.12f, 0.6f, knob.angle, detail::GetPrimaryColorSet(), true,
|
||||
12);
|
||||
break;
|
||||
}
|
||||
|
||||
case ImGuiKnobVariant_Stepped: {
|
||||
for (auto n = 0.f; n < steps; n++) {
|
||||
auto a = n / (steps - 1);
|
||||
auto angle = knob.angle_min + (knob.angle_max - knob.angle_min) * a;
|
||||
knob.draw_tick(0.85f, 1.0f, 0.04f, angle, detail::GetPrimaryColorSet());
|
||||
}
|
||||
|
||||
knob.draw_circle(0.75f, detail::GetSecondaryColorSet(), true, 32);
|
||||
knob.draw_dot(0.12f, 0.4f, knob.angle, detail::GetPrimaryColorSet(), true,
|
||||
12);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return knob.value_changed;
|
||||
}
|
||||
|
||||
bool Knob(const char *label, float *p_value, float v_min, float v_max,
|
||||
float speed, const char *format, ImGuiKnobVariant variant, float size,
|
||||
ImGuiKnobFlags flags, int steps)
|
||||
{
|
||||
const char *_format = format == NULL ? "%.3f" : format;
|
||||
return BaseKnob(label, ImGuiDataType_Float, p_value, v_min, v_max,
|
||||
_format, variant, size, flags, steps);
|
||||
}
|
||||
|
||||
bool KnobInt(const char *label, int *p_value, int v_min, int v_max, float speed,
|
||||
const char *format, ImGuiKnobVariant variant, float size,
|
||||
ImGuiKnobFlags flags, int steps)
|
||||
{
|
||||
const char *_format = format == NULL ? "%i" : format;
|
||||
return BaseKnob(label, ImGuiDataType_S32, p_value, v_min, v_max,
|
||||
_format, variant, size, flags, steps);
|
||||
}
|
||||
|
||||
} // namespace ImGuiKnobs
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Code from:
|
||||
// https://github.com/altschuler/imgui-knobs
|
||||
//
|
||||
// Copyright (c) 2022 Simon Altschuler
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
typedef int ImGuiKnobFlags;
|
||||
|
||||
enum ImGuiKnobFlags_ {
|
||||
ImGuiKnobFlags_NoTitle = 1 << 0,
|
||||
ImGuiKnobFlags_ValueTooltip = 1 << 1,
|
||||
ImGuiKnobFlags_TitleTooltip = 1 << 2,
|
||||
};
|
||||
|
||||
typedef int ImGuiKnobVariant;
|
||||
|
||||
enum ImGuiKnobVariant_ {
|
||||
ImGuiKnobVariant_Tick = 1,
|
||||
ImGuiKnobVariant_Dot,
|
||||
ImGuiKnobVariant_Stepped,
|
||||
};
|
||||
|
||||
namespace ImGuiKnobs {
|
||||
|
||||
struct color_set {
|
||||
ImColor base;
|
||||
ImColor hovered;
|
||||
ImColor active;
|
||||
|
||||
color_set(ImColor base, ImColor hovered, ImColor active) :
|
||||
base(base), hovered(hovered), active(active)
|
||||
{
|
||||
}
|
||||
|
||||
color_set(ImColor color) {
|
||||
base = color;
|
||||
hovered = color;
|
||||
active = color;
|
||||
}
|
||||
};
|
||||
|
||||
bool Knob(const char* label, float* p_value, float v_min, float v_max,
|
||||
float speed = 0, const char* format = NULL,
|
||||
ImGuiKnobVariant variant = ImGuiKnobVariant_Tick, float size = 0,
|
||||
ImGuiKnobFlags flags = 0, int steps = 10);
|
||||
|
||||
bool KnobInt(const char* label, int* p_value, int v_min, int v_max,
|
||||
float speed = 0, const char* format = NULL,
|
||||
ImGuiKnobVariant variant = ImGuiKnobVariant_Tick,
|
||||
float size = 0, ImGuiKnobFlags flags = 0, int steps = 10);
|
||||
|
||||
} // namespace ImGuiKnobs
|
||||
@@ -23,13 +23,15 @@
|
||||
#include "hal/rgbleds.h"
|
||||
#include "definitions.h"
|
||||
|
||||
#include "board.h"
|
||||
|
||||
bool usbChargerLed() { return true; }
|
||||
void ledRed() {}
|
||||
void ledGreen() {}
|
||||
void ledBlue() {}
|
||||
void ledOff() {}
|
||||
bool fsLedState(uint8_t) { return false;}
|
||||
void rgbSetLedColor(unsigned char, unsigned char, unsigned char, unsigned char) {}
|
||||
|
||||
void rgbSetLedColor(uint8_t, uint8_t, uint8_t, uint8_t) {}
|
||||
void rgbLedColorApply() {}
|
||||
|
||||
uint8_t getRGBColorIndex(uint32_t color)
|
||||
@@ -41,24 +43,39 @@ uint8_t getRGBColorIndex(uint32_t color)
|
||||
return 0; // Custom value set with Companion
|
||||
}
|
||||
|
||||
// RGB
|
||||
#define WS2812_BYTES_PER_LED 3
|
||||
#if NUM_FUNCTIONS_SWITCHES > 0
|
||||
static uint32_t _fs_switch_colors[NUM_FUNCTIONS_SWITCHES] = {0};
|
||||
static uint32_t _fs_switch_color_mask = 0;
|
||||
static uint32_t _fs_switch_mask = 0;
|
||||
|
||||
// Maximum number of supported LEDs
|
||||
#if !defined(WS2812_MAX_LEDS)
|
||||
# define WS2812_MAX_LEDS 48
|
||||
#endif
|
||||
|
||||
// Pixel values
|
||||
static uint8_t _led_colors[WS2812_BYTES_PER_LED * WS2812_MAX_LEDS];
|
||||
|
||||
uint32_t rgbGetLedColor(uint8_t led)
|
||||
void fsLedRGB(uint8_t index, uint32_t col)
|
||||
{
|
||||
uint8_t* pixel = &_led_colors[led * WS2812_BYTES_PER_LED];
|
||||
return (pixel[1] << 16) + (pixel[0] << 8) + pixel[2];
|
||||
_fs_switch_color_mask |= (1 << index);
|
||||
_fs_switch_colors[index] = col;
|
||||
}
|
||||
|
||||
uint32_t fsGetLedRGB(uint8_t index)
|
||||
{
|
||||
return rgbGetLedColor(index);
|
||||
return _fs_switch_colors[index];
|
||||
}
|
||||
|
||||
bool fsLedIsColorSet(uint8_t index)
|
||||
{
|
||||
return _fs_switch_color_mask & (1 << index);
|
||||
}
|
||||
|
||||
bool fsLedState(uint8_t index)
|
||||
{
|
||||
return _fs_switch_mask & (1 << index);
|
||||
}
|
||||
|
||||
void fsLedOn(uint8_t index)
|
||||
{
|
||||
_fs_switch_mask |= (1 << index);
|
||||
}
|
||||
|
||||
void fsLedOff(uint8_t index)
|
||||
{
|
||||
_fs_switch_mask &= ~(1 << index);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "opentxsimulator.h"
|
||||
#include "edgetx.h"
|
||||
#include "simulcd.h"
|
||||
#include "simuaudio.h"
|
||||
#include "switches.h"
|
||||
#include "serial.h"
|
||||
#include "myeeprom.h"
|
||||
@@ -89,18 +90,6 @@ void firmwareTraceCb(const char * text)
|
||||
}
|
||||
}
|
||||
|
||||
void fsLedRGB(uint8_t idx, uint32_t color)
|
||||
{
|
||||
}
|
||||
|
||||
void fsLedOn(uint8_t idx)
|
||||
{
|
||||
}
|
||||
|
||||
void fsLedOff(uint8_t idx)
|
||||
{
|
||||
}
|
||||
|
||||
// Serial port handling needs to know about OpenTxSimulator, so we we
|
||||
// need to update what's in simpgmspace.cpp when we have a simulator
|
||||
// to point at.
|
||||
@@ -283,7 +272,7 @@ void OpenTxSimulator::start(const char * filename, bool tests)
|
||||
|
||||
QMutexLocker lckr(&m_mtxSimuMain);
|
||||
QMutexLocker slckr(&m_mtxSettings);
|
||||
startAudio(volumeGain);
|
||||
simuAudioInit();
|
||||
simuStart(tests, simuSdDirectory.toLatin1().constData(),
|
||||
simuSettingsDirectory.toLatin1().constData());
|
||||
|
||||
@@ -301,6 +290,7 @@ void OpenTxSimulator::stop()
|
||||
|
||||
QMutexLocker lckr(&m_mtxSimuMain);
|
||||
simuStop();
|
||||
simuAudioDeInit();
|
||||
|
||||
emit stopped();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,908 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_keycode.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_sdl2.h>
|
||||
#include <imgui_impl_sdlrenderer2.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/rotary_encoder.h"
|
||||
#include "hal/switch_driver.h"
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2,0,19)
|
||||
#error This backend requires SDL 2.0.19+ because of SDL_RenderGeometryRaw() function
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#include "stb_image.h"
|
||||
|
||||
#if defined(STBI_NO_STDIO)
|
||||
#error "STBI_NO_STDIO is defined"
|
||||
#endif
|
||||
|
||||
#include "simu.h"
|
||||
#include "widgets.h"
|
||||
#include "knobs.h"
|
||||
#include "display.h"
|
||||
|
||||
#include "simuaudio.h"
|
||||
#include "simpgmspace.h"
|
||||
|
||||
#include "hal/key_driver.h"
|
||||
#include "switches.h"
|
||||
|
||||
#include "audio.h"
|
||||
#include "debug.h"
|
||||
#include "edgetx.h"
|
||||
|
||||
#define TIMER_INTERVAL 10 // 10ms
|
||||
|
||||
int window_width = 800;
|
||||
int window_height = 600;
|
||||
|
||||
std::string storage_path;
|
||||
std::string settings_path;
|
||||
|
||||
static SDL_Window* window;
|
||||
static SDL_Renderer* renderer;
|
||||
static SDL_Texture* screen_frame_buffer;
|
||||
|
||||
static GimbalState stick_left = {{0.5f, 0.5f}, false};
|
||||
static GimbalState stick_right = {{0.5f, 0.5f}, false};
|
||||
|
||||
static const unsigned char _icon_png[] = {
|
||||
#include "icon.lbm"
|
||||
};
|
||||
|
||||
#if defined(ROTARY_ENCODER_NAVIGATION)
|
||||
extern volatile rotenc_t rotencValue;
|
||||
#endif
|
||||
|
||||
int pots[MAX_POTS] = {0};
|
||||
|
||||
static bool handleKeyEvents(SDL_Event& event)
|
||||
{
|
||||
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
|
||||
const auto& key_event = event.key;
|
||||
|
||||
bool key_handled = false;
|
||||
uint8_t key = 0;
|
||||
|
||||
switch(key_event.keysym.sym) {
|
||||
|
||||
case SDLK_ESCAPE:
|
||||
key = KEY_EXIT;
|
||||
key_handled = true;
|
||||
break;
|
||||
|
||||
case SDLK_RETURN:
|
||||
key = KEY_ENTER;
|
||||
key_handled = true;
|
||||
break;
|
||||
|
||||
case SDLK_LEFT:
|
||||
if (keysGetSupported() & (1 << KEY_LEFT)) {
|
||||
key = KEY_LEFT;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_RIGHT:
|
||||
if (keysGetSupported() & (1 << KEY_RIGHT)) {
|
||||
key = KEY_RIGHT;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_UP:
|
||||
#if defined(ROTARY_ENCODER_NAVIGATION)
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
rotencValue -= ROTARY_ENCODER_GRANULARITY;
|
||||
}
|
||||
#else
|
||||
if (keysGetSupported() & (1 << KEY_UP)) {
|
||||
key = KEY_UP;
|
||||
key_handled = true;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SDLK_DOWN:
|
||||
#if defined(ROTARY_ENCODER_NAVIGATION)
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
rotencValue += ROTARY_ENCODER_GRANULARITY;
|
||||
}
|
||||
#else
|
||||
if (keysGetSupported() & (1 << KEY_DOWN)) {
|
||||
key = KEY_DOWN;
|
||||
key_handled = true;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SDLK_PLUS:
|
||||
if (keysGetSupported() & (1 << KEY_PLUS)) {
|
||||
key = KEY_PLUS;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_MINUS:
|
||||
if (keysGetSupported() & (1 << KEY_MINUS)) {
|
||||
key = KEY_MINUS;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_PAGEUP:
|
||||
if (keysGetSupported() & (1 << KEY_PAGEUP)) {
|
||||
key = KEY_PAGEUP;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_PAGEDOWN:
|
||||
if (keysGetSupported() & (1 << KEY_PAGEDN)) {
|
||||
key = KEY_PAGEDN;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_m:
|
||||
if (keysGetSupported() & (1 << KEY_MENU)) {
|
||||
key = KEY_MENU;
|
||||
key_handled = true;
|
||||
} else if (keysGetSupported() & (1 << KEY_MODEL)) {
|
||||
key = KEY_MODEL;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_s:
|
||||
if (keysGetSupported() & (1 << KEY_SYS)) {
|
||||
key = KEY_SYS;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_t:
|
||||
if (keysGetSupported() & (1 << KEY_TELE)) {
|
||||
key = KEY_TELE;
|
||||
key_handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_l:
|
||||
ImGui::StyleColorsLight();
|
||||
break;
|
||||
|
||||
case SDLK_d:
|
||||
ImGui::StyleColorsDark();
|
||||
break;
|
||||
|
||||
case SDLK_r:
|
||||
if (event.type == SDL_KEYUP) {
|
||||
simuStop();
|
||||
simuStart();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
key_handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (key_handled) {
|
||||
simuSetKey(key, key_event.type == SDL_KEYDOWN ? true : false);
|
||||
}
|
||||
|
||||
return key_handled;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void redraw();
|
||||
|
||||
static bool handleEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
|
||||
if(handleKeyEvents(event))
|
||||
continue;
|
||||
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
|
||||
if (event.type == SDL_QUIT)
|
||||
return false;
|
||||
|
||||
if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(window))
|
||||
return false;
|
||||
}
|
||||
|
||||
redraw();
|
||||
return true;
|
||||
}
|
||||
|
||||
static SDL_Surface* LoadImage(const unsigned char* pixels, size_t len)
|
||||
{
|
||||
// Read data
|
||||
int32_t w, h, bpp;
|
||||
|
||||
void* data = stbi_load_from_memory(pixels, len, &w, &h, &bpp, 0);
|
||||
if (!data) return NULL;
|
||||
|
||||
Uint32 format = (bpp == 3) ? SDL_PIXELFORMAT_RGB24 : SDL_PIXELFORMAT_RGBA32;
|
||||
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, bpp * 8, format);
|
||||
if (surface) {
|
||||
SDL_LockSurface(surface);
|
||||
memcpy(surface->pixels, data, w * h * bpp);
|
||||
SDL_UnlockSurface(surface);
|
||||
}
|
||||
|
||||
stbi_image_free(data);
|
||||
return surface;
|
||||
}
|
||||
|
||||
static SDL_Texture* LoadTexture(SDL_Renderer* renderer, const unsigned char* pixels, size_t len)
|
||||
{
|
||||
SDL_Surface* surface = LoadImage(pixels, len);
|
||||
if (!surface) return NULL;
|
||||
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_FreeSurface(surface);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
static float switches_width()
|
||||
{
|
||||
const int max_sw_line = MAX_SWITCHES / 2;
|
||||
const int sw_line = std::min((int)switchGetMaxSwitches(), max_sw_line);
|
||||
return sw_line * 18 + (sw_line - 1) * 4;
|
||||
}
|
||||
|
||||
static void draw_switches()
|
||||
{
|
||||
const float spacing = 4;
|
||||
const ImVec2 sw_size(18, 50);
|
||||
|
||||
ImGui::PushID("switches");
|
||||
{
|
||||
static int switches[MAX_SWITCHES] = {0};
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 4.0f);
|
||||
|
||||
int sw_idx = 0;
|
||||
for (int i = 0; i < switchGetMaxSwitches(); i++) {
|
||||
if (sw_idx) ImGui::SameLine();
|
||||
if (++sw_idx >= MAX_SWITCHES / 2) sw_idx = 0;
|
||||
|
||||
if (!SWITCH_EXISTS(i)) {
|
||||
switches[i] = 0;
|
||||
ImGui::Dummy(sw_size);
|
||||
} else {
|
||||
ImGui::PushID(i);
|
||||
ImGui::VSliderInt("##sw", sw_size,
|
||||
&switches[i], IS_CONFIG_3POS(i) ? 2 : 1,
|
||||
0, "", ImGuiSliderFlags_NoInput);
|
||||
if (ImGui::IsItemActive() || ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", switchGetCanonicalName(i));
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (IS_CONFIG_3POS(i)) {
|
||||
simuSetSwitch(i, switches[i] == 0 ? -1 : switches[i] == 1 ? 0 : 1);
|
||||
} else {
|
||||
simuSetSwitch(i, switches[i] == 0 ? -1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
extern bool fsLedIsColorSet(uint8_t index);
|
||||
|
||||
static ImVec4 rgb2rgba(uint32_t col)
|
||||
{
|
||||
float r = (float)((col >> 16) & 0xff) / 255.0f;
|
||||
float g = (float)((col >> 8) & 0xff) / 255.0f;
|
||||
float b = (float)((col >> 0) & 0xff) / 255.0f;
|
||||
return {r, g, b, 1.0f};
|
||||
}
|
||||
|
||||
static inline ImVec4 blend(const ImVec4& a, const ImVec4& b)
|
||||
{
|
||||
float w0 = a.w + b.w;
|
||||
return ImVec4((a.x * a.w + b.x * b.w) / w0, (a.y * a.w + b.y * b.w) / w0,
|
||||
(a.z * a.w + b.z * b.w) / w0, 1.0f);
|
||||
}
|
||||
|
||||
static void push_custom_switch_styles(int index)
|
||||
{
|
||||
bool sw_on = getFSLogicalState(index);
|
||||
bool rgb_set = fsLedIsColorSet(index);
|
||||
ImVec4 rgb = rgb2rgba(fsGetLedRGB(index));
|
||||
|
||||
ImVec4 btn, border;
|
||||
if (sw_on) {
|
||||
btn = rgb_set ? rgb : ImGui::GetStyleColorVec4(ImGuiCol_SliderGrab);
|
||||
border = ImGui::GetStyleColorVec4(ImGuiCol_SliderGrab);
|
||||
} else {
|
||||
btn = rgb_set ? rgb : ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
|
||||
border = ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
|
||||
}
|
||||
|
||||
// color intensity
|
||||
float btn_int = (btn.x + btn.y + btn.z) / 3.0f;
|
||||
float inv_int = 1.0f - btn_int;
|
||||
ImVec4 mask(inv_int, inv_int, inv_int, 0.3f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, btn);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, blend(btn, mask));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, border);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 3.0f);
|
||||
}
|
||||
|
||||
static void pop_custom_switch_styles()
|
||||
{
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
static void draw_custom_switches()
|
||||
{
|
||||
const float spacing = 4;
|
||||
const ImVec2 sw_size(18, 18);
|
||||
|
||||
ImGui::PushID("custom switches");
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 4.0f);
|
||||
|
||||
int sw_idx = switchGetMaxSwitches();
|
||||
for (int i = 0; i < switchGetMaxFctSwitches(); i++, sw_idx++) {
|
||||
if (i) ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
|
||||
push_custom_switch_styles(i);
|
||||
ImGui::Button("##csw", sw_size);
|
||||
pop_custom_switch_styles();
|
||||
|
||||
bool active = ImGui::IsItemActive();
|
||||
if (active || ImGui::IsItemHovered()) {
|
||||
const char* name = switchGetCanonicalName(sw_idx);
|
||||
const char* on_off = getFSLogicalState(i) ? "on" : "off";
|
||||
ImGui::SetTooltip("%s (%s)", name, on_off);
|
||||
}
|
||||
simuSetSwitch(sw_idx, active ? 1 : -1);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
#endif
|
||||
|
||||
static float gimbals_width()
|
||||
{
|
||||
return 244.0f;
|
||||
}
|
||||
|
||||
static void draw_gimbals()
|
||||
{
|
||||
stick_left.lock_y = (g_eeGeneral.stickMode == 1);
|
||||
stick_right.lock_y = (g_eeGeneral.stickMode == 0);
|
||||
|
||||
GimbalPair("#gimbals", stick_left, stick_right);
|
||||
}
|
||||
|
||||
static float pots_width()
|
||||
{
|
||||
const float spacing = 2;
|
||||
float pot_width = ImGui::GetTextLineHeight() * 4.0f;
|
||||
int pots = std::min((int)adcGetMaxInputs(ADC_INPUT_FLEX), 5);
|
||||
return pots * pot_width + (pots - 1) * spacing +
|
||||
2 * ImGui::GetStyle().CellPadding.x;
|
||||
}
|
||||
|
||||
static void draw_pots()
|
||||
{
|
||||
const float spacing = 2;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
|
||||
ImGui::PushID("pots");
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
int pot_idx = 0;
|
||||
for (int i = 0; i < adcGetMaxInputs(ADC_INPUT_FLEX); i++) {
|
||||
if (pot_idx > 0) ImGui::SameLine();
|
||||
if (++pot_idx >= 5) pot_idx = 0;
|
||||
|
||||
if (!IS_POT_AVAILABLE(i)) {
|
||||
pots[i] = 0;
|
||||
auto sz = ImGui::GetTextLineHeight() * 4.0f;
|
||||
ImGui::Dummy({sz, sz});
|
||||
} else {
|
||||
ImGui::PushID(i);
|
||||
auto flags = ImGuiKnobFlags_NoTitle | ImGuiKnobFlags_ValueTooltip |
|
||||
ImGuiKnobFlags_TitleTooltip;
|
||||
auto label = adcGetInputLabel(ADC_INPUT_FLEX, i);
|
||||
switch(getPotType(i)) {
|
||||
case FLEX_POT:
|
||||
case FLEX_POT_CENTER:
|
||||
case FLEX_SLIDER:
|
||||
ImGuiKnobs::KnobInt(label, &pots[i], -100, 100, 1, "%d",
|
||||
ImGuiKnobVariant_Tick, 0, flags);
|
||||
break;
|
||||
|
||||
case FLEX_MULTIPOS:
|
||||
ImGuiKnobs::KnobInt(label, &pots[i], 0, 5, 0.2f, "%d",
|
||||
ImGuiKnobVariant_Stepped, 0, flags, 6);
|
||||
break;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void draw_controls()
|
||||
{
|
||||
ImGui::BeginTable("controls", 3, ImGuiTableFlags_SizingFixedFit);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, switches_width());
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, gimbals_width());
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, pots_width());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
draw_switches();
|
||||
#if defined(FUNCTION_SWITCHES)
|
||||
draw_custom_switches();
|
||||
#endif
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
draw_gimbals();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
draw_pots();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImU32 get_bg_color()
|
||||
{
|
||||
if (LCD_DEPTH < 16) {
|
||||
if (isBacklightEnabled()) {
|
||||
return IM_COL32(47, 123, 227, 255);
|
||||
} else {
|
||||
return IM_COL32(200, 200, 200, 255);
|
||||
}
|
||||
} else {
|
||||
return IM_COL32_BLACK_TRANS;
|
||||
}
|
||||
}
|
||||
|
||||
static ImVec2 calc_screen_size()
|
||||
{
|
||||
float width, height;
|
||||
|
||||
height = ImGui::GetContentRegionAvail().y;
|
||||
width = height * float(LCD_W) / float(LCD_H);
|
||||
|
||||
if (width > ImGui::GetContentRegionAvail().x) {
|
||||
width = ImGui::GetContentRegionAvail().x;
|
||||
height = width * float(LCD_H) / float(LCD_W);
|
||||
}
|
||||
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
static void draw_screen()
|
||||
{
|
||||
const ScreenDesc desc = {
|
||||
.width = LCD_W,
|
||||
.height = LCD_H,
|
||||
.is_dot_matrix = LCD_DEPTH == 1 || LCD_DEPTH == 4,
|
||||
};
|
||||
|
||||
auto size = calc_screen_size();
|
||||
SimuScreen(desc, (ImTextureID)screen_frame_buffer, size, get_bg_color(),
|
||||
(LCD_DEPTH == 16 && !isBacklightEnabled()) ?
|
||||
IM_COL32(0,0,0,127) : IM_COL32_BLACK_TRANS);
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
ScreenMouseEvent touch_event;
|
||||
if (SimuScreenMouseEvent(desc, touch_event)) {
|
||||
if (touch_event.type == ScreenMouseEventType::MouseDown) {
|
||||
touchPanelDown(touch_event.pos_x, touch_event.pos_y);
|
||||
} else {
|
||||
touchPanelUp();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int get_used_window_height()
|
||||
{
|
||||
return (int)ImGui::GetCursorScreenPos().y;
|
||||
}
|
||||
|
||||
static int calc_min_width()
|
||||
{
|
||||
return switches_width() + gimbals_width() + pots_width() +
|
||||
6 * ImGui::GetStyle().CellPadding.x;
|
||||
}
|
||||
|
||||
static void redraw()
|
||||
{
|
||||
refreshDisplay(screen_frame_buffer);
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
static ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoTitleBar;
|
||||
|
||||
// Use full work area (without menu-bars, task-bars etc.)
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||
|
||||
bool show_win = true;
|
||||
|
||||
if (ImGui::Begin("Main window", &show_win, flags)) {
|
||||
draw_controls();
|
||||
draw_screen();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
|
||||
auto bg_col = ImGui::GetColorU32(ImGuiCol_WindowBg);
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
(bg_col >> IM_COL32_R_SHIFT) & 0xFF,
|
||||
(bg_col >> IM_COL32_G_SHIFT) & 0xFF,
|
||||
(bg_col >> IM_COL32_B_SHIFT) & 0xFF,
|
||||
(bg_col >> IM_COL32_A_SHIFT) & 0xFF);
|
||||
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
// Adjust window size
|
||||
if (show_win) {
|
||||
int min_w = calc_min_width();
|
||||
int w, h;
|
||||
SDL_GetWindowMinimumSize(window, &w, &h);
|
||||
if (w < min_w || h < 400) {
|
||||
SDL_Log("Window width: %d < %d", w, min_w);
|
||||
SDL_SetWindowMinimumSize(window, min_w, 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int default_input_mode()
|
||||
{
|
||||
#if defined(DEFAULT_MODE)
|
||||
return DEFAULT_MODE - 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int find_input_mode()
|
||||
{
|
||||
// TODO: add support for path from command line
|
||||
std::ifstream file("./RADIO/radio.yml");
|
||||
if (!file) return default_input_mode();
|
||||
|
||||
std::regex re("^stickMode:\\s*(\\d+)");
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
std::smatch matches;
|
||||
if (std::regex_search(line, matches, re) && matches.size() > 1) {
|
||||
return std::stoi(matches[1].str());
|
||||
}
|
||||
}
|
||||
|
||||
return default_input_mode();
|
||||
}
|
||||
|
||||
static void print_usage(const char* path)
|
||||
{
|
||||
const char usage[] =
|
||||
"usage: %s [--width width] [--height height] [--storage path] "
|
||||
"[--settings path] [-h | --help]\n";
|
||||
|
||||
printf(usage, basename((char*)path));
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char* argv[])
|
||||
{
|
||||
static struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"width", required_argument, 0, 'w'},
|
||||
{"height", required_argument, 0, 'e'},
|
||||
{"storage", required_argument, 0, 's'},
|
||||
{"settings", required_argument, 0, 'c'},
|
||||
{0, 0, 0, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
int c;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", long_options, &option_index)) !=
|
||||
-1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
|
||||
case 'w':
|
||||
window_width = std::atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
window_height = std::atoi(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
storage_path = std::string(optarg);
|
||||
break;
|
||||
|
||||
case 'c': // 'c' for config/settings
|
||||
settings_path = std::string(optarg);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
// getopt_long already printed an error message
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for non-option arguments
|
||||
if (optind < argc) {
|
||||
printf("Error: Unexpected arguments: ");
|
||||
while (optind < argc) {
|
||||
printf("%s ", argv[optind++]);
|
||||
}
|
||||
printf("\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (parse_args(argc, argv) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
simuAudioInit();
|
||||
|
||||
// From 2.0.18: Enable native IME.
|
||||
#ifdef SDL_HINT_IME_SHOW_UI
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
#endif
|
||||
|
||||
// Create window with SDL_Renderer graphics context
|
||||
int window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
window = SDL_CreateWindow("EdgeTx Simu", SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, window_width, window_height,
|
||||
window_flags);
|
||||
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (renderer == NULL) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "cannot create SDL_Renderer!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRendererInfo(renderer, &info);
|
||||
SDL_Log("Current SDL_Renderer: %s", info.name);
|
||||
|
||||
SDL_Surface* icon = LoadImage(_icon_png, sizeof(_icon_png));
|
||||
if (window && icon) {
|
||||
SDL_SetWindowIcon(window, icon);
|
||||
}
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsLight();
|
||||
// ImGui::StyleColorsDark();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
|
||||
ImGui_ImplSDLRenderer2_Init(renderer);
|
||||
|
||||
// Create textures for radio screen
|
||||
screen_frame_buffer =
|
||||
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, LCD_W, LCD_H);
|
||||
|
||||
if (!screen_frame_buffer) {
|
||||
SDL_Log("Could not create frame buffer textures");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// SDL_SetTextureScaleMode(screen_frame_buffer, SDL_ScaleModeBest);
|
||||
SDL_SetTextureBlendMode(screen_frame_buffer, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// Init gimbal safe position
|
||||
int input_mode = find_input_mode();
|
||||
switch (input_mode) {
|
||||
case 0:
|
||||
case 2:
|
||||
stick_right.pos.y = 1.0f;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
stick_left.pos.y = 1.0f;
|
||||
break;
|
||||
default:
|
||||
SDL_Log("Invalid input mode %d", input_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Init simulation
|
||||
simuInit();
|
||||
simuStart(true, storage_path.c_str(), settings_path.c_str());
|
||||
|
||||
// Main loop
|
||||
SDL_SetEventFilter([](void*, SDL_Event* event){
|
||||
if (event->type == SDL_WINDOWEVENT &&
|
||||
(event->window.event == SDL_WINDOWEVENT_EXPOSED)) {
|
||||
redraw();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}, NULL);
|
||||
|
||||
do {
|
||||
Uint64 start_ts = SDL_GetPerformanceCounter();
|
||||
if (!handleEvents()) break;
|
||||
|
||||
Uint64 end_ts = SDL_GetPerformanceCounter();
|
||||
float elapsedMS =
|
||||
(end_ts - start_ts) / (float)SDL_GetPerformanceFrequency() * 1000.0f;
|
||||
|
||||
// Cap to 60 FPS
|
||||
SDL_Delay(std::max(0,(int32_t)floor(16.666f - elapsedMS)));
|
||||
|
||||
} while(true);
|
||||
|
||||
// App cleanup
|
||||
simuStop();
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyTexture(screen_frame_buffer);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_FreeSurface(icon);
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t simu_get_analog(uint8_t idx)
|
||||
{
|
||||
auto max_sticks = adcGetMaxInputs(ADC_INPUT_MAIN);
|
||||
if (idx < max_sticks) {
|
||||
switch(idx) {
|
||||
case 0: return stick_left.pos.x * 4096;
|
||||
case 1: return (1.0 - stick_left.pos.y) * 4096;
|
||||
case 2: return (1.0 - stick_right.pos.y) * 4096;
|
||||
case 3: return stick_right.pos.x * 4096;
|
||||
}
|
||||
}
|
||||
|
||||
idx -= max_sticks;
|
||||
|
||||
auto max_pots = adcGetMaxInputs(ADC_INPUT_FLEX);
|
||||
if (idx < max_pots) {
|
||||
switch(getPotType(idx)){
|
||||
case FLEX_POT:
|
||||
case FLEX_POT_CENTER:
|
||||
case FLEX_SLIDER:
|
||||
return uint16_t(((uint32_t(pots[idx]) + 100) * 4096) / 200);
|
||||
case FLEX_MULTIPOS:
|
||||
return (uint32_t(pots[idx]) * 4096) / 5;
|
||||
}
|
||||
}
|
||||
|
||||
// idx -= max_pots;
|
||||
|
||||
// auto max_axes = adcGetMaxInputs(ADC_INPUT_AXIS);
|
||||
// if (idx < max_axes) return 0;
|
||||
|
||||
// probably RTC_BAT
|
||||
return 0;
|
||||
}
|
||||
@@ -127,6 +127,8 @@ void simuInit()
|
||||
|
||||
// Init ADC driver callback
|
||||
adcInit(&simu_adc_driver);
|
||||
// Switches
|
||||
switchInit();
|
||||
}
|
||||
|
||||
bool keysStates[MAX_KEYS] = { false };
|
||||
@@ -205,12 +207,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SIMU_EXCEPTIONS)
|
||||
signal(SIGFPE, sig);
|
||||
signal(SIGSEGV, sig);
|
||||
try {
|
||||
#endif
|
||||
|
||||
// Init LCD callbacks
|
||||
lcdInit();
|
||||
|
||||
@@ -228,12 +224,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
|
||||
#endif
|
||||
|
||||
simu_running = true;
|
||||
|
||||
#if defined(SIMU_EXCEPTIONS)
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern task_handle_t mixerTaskId;
|
||||
@@ -250,130 +240,14 @@ void simuStop()
|
||||
simu_shutdown = true;
|
||||
task_shutdown_all();
|
||||
|
||||
#if defined(SIMU_AUDIO)
|
||||
stopAudio();
|
||||
#endif
|
||||
|
||||
simu_running = false;
|
||||
}
|
||||
|
||||
struct SimulatorAudio {
|
||||
int volumeGain;
|
||||
int currentVolume;
|
||||
int16_t leftoverData[AUDIO_BUFFER_SIZE];
|
||||
int leftoverLen;
|
||||
} simuAudio;
|
||||
|
||||
bool simuIsRunning()
|
||||
{
|
||||
return simu_running;
|
||||
}
|
||||
|
||||
void audioConsumeCurrentBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void audioSetVolume(uint8_t volume)
|
||||
{
|
||||
simuAudio.currentVolume = 127 * volume * simuAudio.volumeGain / VOLUME_LEVEL_MAX / 10;
|
||||
// TRACE_SIMPGMSPACE("setVolume(): in: %u, out: %u", volume, simuAudio.currentVolume);
|
||||
}
|
||||
|
||||
#if defined(SIMU_AUDIO)
|
||||
void copyBuffer(void* dest, const int16_t* buff, unsigned samples)
|
||||
{
|
||||
int16_t* i16_dst = (int16_t*)dest;
|
||||
for (unsigned i = 0; i < samples; i++) {
|
||||
int32_t sample = (((int32_t)buff[i] * (int32_t)simuAudio.currentVolume) / 127);
|
||||
if (sample > INT16_MAX) sample = INT16_MAX;
|
||||
else if (sample < INT16_MIN) sample = INT16_MIN;
|
||||
*(i16_dst++) = (int16_t)sample;
|
||||
}
|
||||
}
|
||||
|
||||
void fillAudioBuffer(void *udata, Uint8 *stream, int len)
|
||||
{
|
||||
SDL_memset(stream, 0, len);
|
||||
|
||||
if (simuAudio.leftoverLen) {
|
||||
int len1 = min(len/2, simuAudio.leftoverLen);
|
||||
copyBuffer(stream, simuAudio.leftoverData, len1);
|
||||
len -= len1*2;
|
||||
stream += len1*2;
|
||||
simuAudio.leftoverLen -= len1;
|
||||
// putchar('l');
|
||||
if (simuAudio.leftoverLen) return; // buffer fully filled
|
||||
}
|
||||
|
||||
if (audioQueue.buffersFifo.filledAtleast(len / (AUDIO_BUFFER_SIZE * 2) + 1)) {
|
||||
while (true) {
|
||||
const AudioBuffer* nextBuffer =
|
||||
audioQueue.buffersFifo.getNextFilledBuffer();
|
||||
if (nextBuffer) {
|
||||
if (len >= nextBuffer->size * 2) {
|
||||
copyBuffer(stream, nextBuffer->data, nextBuffer->size);
|
||||
stream += nextBuffer->size * 2;
|
||||
len -= nextBuffer->size * 2;
|
||||
// putchar('+');
|
||||
audioQueue.buffersFifo.freeNextFilledBuffer();
|
||||
} else {
|
||||
// partial
|
||||
copyBuffer(stream, nextBuffer->data, len / 2);
|
||||
simuAudio.leftoverLen = (nextBuffer->size - len / 2);
|
||||
memcpy(simuAudio.leftoverData, &nextBuffer->data[len / 2],
|
||||
simuAudio.leftoverLen * 2);
|
||||
len = 0;
|
||||
// putchar('p');
|
||||
audioQueue.buffersFifo.freeNextFilledBuffer();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill the rest of buffer with silence
|
||||
if (len > 0) {
|
||||
SDL_memset(stream, 0x8000, len); // make sure this is silence.
|
||||
}
|
||||
}
|
||||
|
||||
int startAudio(int volumeGain)
|
||||
{
|
||||
simuAudio = {
|
||||
.volumeGain = volumeGain,
|
||||
.leftoverLen = 0,
|
||||
};
|
||||
|
||||
TRACE("startAudioThread(%d)", volumeGain);
|
||||
audioSetVolume(VOLUME_LEVEL_DEF);
|
||||
|
||||
/* Set the audio format */
|
||||
SDL_AudioSpec desired = {
|
||||
.freq = AUDIO_SAMPLE_RATE,
|
||||
.format = AUDIO_S16SYS,
|
||||
.channels = 1,
|
||||
.samples = AUDIO_BUFFER_SIZE * 2,
|
||||
.callback = fillAudioBuffer,
|
||||
.userdata = nullptr,
|
||||
};
|
||||
|
||||
SDL_AudioSpec obtained;
|
||||
if ( SDL_OpenAudio(&desired, &obtained) < 0 ) {
|
||||
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
SDL_PauseAudio(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stopAudio()
|
||||
{
|
||||
SDL_CloseAudio();
|
||||
}
|
||||
#endif // #if defined(SIMU_AUDIO)
|
||||
|
||||
#if !defined(COLORLCD)
|
||||
void lcdSetRefVolt(uint8_t val)
|
||||
{
|
||||
@@ -617,6 +491,12 @@ const etx_serial_port_t* auxSerialGetPort(int port_nr)
|
||||
struct TouchState simTouchState = {};
|
||||
bool simTouchOccured = false;
|
||||
|
||||
bool touchPanelInit()
|
||||
{
|
||||
simTouchState.x = simTouchState.y = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchPanelEventOccured()
|
||||
{
|
||||
if(simTouchOccured)
|
||||
@@ -627,6 +507,20 @@ bool touchPanelEventOccured()
|
||||
return false;
|
||||
}
|
||||
|
||||
void touchPanelDown(short x, short y)
|
||||
{
|
||||
simTouchState.x = x;
|
||||
simTouchState.y = y;
|
||||
simTouchState.event = TE_DOWN;
|
||||
simTouchOccured = true;
|
||||
}
|
||||
|
||||
void touchPanelUp()
|
||||
{
|
||||
simTouchState.event = TE_UP;
|
||||
simTouchOccured = true;
|
||||
}
|
||||
|
||||
struct TouchState touchPanelRead()
|
||||
{
|
||||
struct TouchState st = simTouchState;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#include "gui/colorlcd/lcd.h"
|
||||
#endif
|
||||
|
||||
#include "targets/simu/simulcd.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/rotary_encoder.h"
|
||||
|
||||
// 10ms ISR
|
||||
void per10ms();
|
||||
|
||||
// touch panel methods
|
||||
bool touchPanelInit();
|
||||
void touchPanelDown(short x, short y);
|
||||
void touchPanelUp();
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "audio.h"
|
||||
#include "simuaudio.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#define SIMU_AUDIO_FMT AUDIO_S16SYS
|
||||
|
||||
static SDL_AudioDeviceID _sdl_audio_device = 0;
|
||||
|
||||
#if !defined(SOFTWARE_VOLUME)
|
||||
|
||||
static Uint8 _tmp_buf[AUDIO_BUFFER_SIZE * sizeof(audio_data_t)];
|
||||
static int _simu_volume = 0;
|
||||
|
||||
void audioSetVolume(uint8_t volume)
|
||||
{
|
||||
_simu_volume = SDL_MIX_MAXVOLUME * volume / VOLUME_LEVEL_MAX;
|
||||
}
|
||||
|
||||
static void _fill_with_silence(Uint8* dst, uint32_t len)
|
||||
{
|
||||
auto p = (audio_data_t*)dst;
|
||||
auto end = p + len / sizeof(audio_data_t);
|
||||
|
||||
while (p < end) {
|
||||
*p++ = AUDIO_DATA_SILENCE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void audioConsumeCurrentBuffer()
|
||||
{
|
||||
while(true) {
|
||||
auto nextBuffer = audioQueue.buffersFifo.getNextFilledBuffer();
|
||||
if (!nextBuffer) return;
|
||||
|
||||
auto data = (const Uint8*)nextBuffer->data;
|
||||
uint32_t len = nextBuffer->size * sizeof(audio_data_t);
|
||||
|
||||
#if !defined(SOFTWARE_VOLUME)
|
||||
assert(len <= sizeof(_tmp_buf));
|
||||
_fill_with_silence(_tmp_buf, len);
|
||||
|
||||
SDL_MixAudioFormat(_tmp_buf, data, SIMU_AUDIO_FMT, len, _simu_volume);
|
||||
data = _tmp_buf;
|
||||
#endif
|
||||
|
||||
SDL_QueueAudio(_sdl_audio_device, data, len);
|
||||
audioQueue.buffersFifo.freeNextFilledBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
bool simuAudioInit()
|
||||
{
|
||||
SDL_AudioSpec wanted = {
|
||||
.freq = AUDIO_SAMPLE_RATE,
|
||||
.format = SIMU_AUDIO_FMT,
|
||||
.channels = 1,
|
||||
.silence = 0,
|
||||
.samples = 1024,
|
||||
};
|
||||
|
||||
SDL_AudioSpec have;
|
||||
_sdl_audio_device = SDL_OpenAudioDevice(0, 0, &wanted, &have, 0);
|
||||
if (!_sdl_audio_device) {
|
||||
SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(_sdl_audio_device, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void simuAudioDeInit()
|
||||
{
|
||||
if(_sdl_audio_device > 0) {
|
||||
SDL_CloseAudioDevice(_sdl_audio_device);
|
||||
_sdl_audio_device = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
bool simuAudioInit();
|
||||
void simuAudioDeInit();
|
||||
@@ -20,272 +20,102 @@
|
||||
*/
|
||||
|
||||
#if defined(SIMU_DISKIO)
|
||||
#include "edgetx.h"
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
#include <time.h>
|
||||
#include "hal/fatfs_diskio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
FILE * diskImage = 0;
|
||||
extern const diskio_driver_t simu_diskio_driver;
|
||||
|
||||
bool _g_FATFS_init = false;
|
||||
static FILE* disk_image = nullptr;
|
||||
|
||||
RTOS_MUTEX_HANDLE ioMutex;
|
||||
// create FS with: mkdosfs -n SDCARD -S 512 -C sdcard.img 524288
|
||||
static const char* image_path = "sdcard.img";
|
||||
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj) /* Create a sync object */
|
||||
static int _seek(DWORD sector)
|
||||
{
|
||||
pthread_mutex_init(&ioMutex, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ff_req_grant (FF_SYNC_t sobj) /* Lock sync object */
|
||||
{
|
||||
pthread_mutex_lock(&ioMutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ff_rel_grant (FF_SYNC_t sobj) /* Unlock sync object */
|
||||
{
|
||||
pthread_mutex_unlock(&ioMutex);
|
||||
}
|
||||
|
||||
int ff_del_syncobj (FF_SYNC_t sobj) /* Delete a sync object */
|
||||
{
|
||||
pthread_mutex_destroy(&ioMutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD get_fattime (void)
|
||||
{
|
||||
time_t tim = time(0);
|
||||
const struct tm * t = gmtime(&tim);
|
||||
|
||||
/* Pack date and time into a DWORD variable */
|
||||
return ((DWORD)(t->tm_year - 80) << 25)
|
||||
| ((uint32_t)(t->tm_mon+1) << 21)
|
||||
| ((uint32_t)t->tm_mday << 16)
|
||||
| ((uint32_t)t->tm_hour << 11)
|
||||
| ((uint32_t)t->tm_min << 5)
|
||||
| ((uint32_t)t->tm_sec >> 1);
|
||||
}
|
||||
|
||||
unsigned int noDiskStatus = 0;
|
||||
|
||||
void traceDiskStatus()
|
||||
{
|
||||
if (noDiskStatus > 0) {
|
||||
TRACE_SIMPGMSPACE("disk_status() called %d times", noDiskStatus);
|
||||
noDiskStatus = 0;
|
||||
if (!disk_image) {
|
||||
fprintf(stderr, "disk image is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(disk_image, sector * 512, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv)
|
||||
static DSTATUS simu_disk_initialize(BYTE lun)
|
||||
{
|
||||
traceDiskStatus();
|
||||
TRACE_SIMPGMSPACE("disk_initialize(%u)", pdrv);
|
||||
diskImage = fopen("sdcard.image", "rb+");
|
||||
return diskImage ? (DSTATUS)0 : (DSTATUS)STA_NODISK;
|
||||
}
|
||||
if (!image_path) return STA_NODISK;
|
||||
disk_image = fopen(image_path, "rb+");
|
||||
if (!disk_image) {
|
||||
fprintf(stderr, "could not open disk image: %s\n", strerror(errno));
|
||||
return STA_NODISK;
|
||||
}
|
||||
|
||||
DSTATUS disk_status (BYTE pdrv)
|
||||
{
|
||||
++noDiskStatus;
|
||||
// TRACE_SIMPGMSPACE("disk_status(%u)", pdrv);
|
||||
return (DSTATUS)0;
|
||||
}
|
||||
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
if (diskImage == 0) return RES_NOTRDY;
|
||||
traceDiskStatus();
|
||||
TRACE_SIMPGMSPACE("disk_read(%u, %p, %u, %u)", pdrv, buff, sector, count);
|
||||
fseek(diskImage, sector*512, SEEK_SET);
|
||||
fread(buff, count, 512, diskImage);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
static DSTATUS simu_disk_status(BYTE lun) { return RES_OK; }
|
||||
|
||||
static DRESULT simu_disk_read(BYTE lun, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
if (diskImage == 0) return RES_NOTRDY;
|
||||
traceDiskStatus();
|
||||
TRACE_SIMPGMSPACE("disk_write(%u, %p, %u, %u)", pdrv, buff, sector, count);
|
||||
fseek(diskImage, sector*512, SEEK_SET);
|
||||
fwrite(buff, count, 512, diskImage);
|
||||
if (_seek(sector) != 0) return RES_ERROR;
|
||||
fprintf(stderr, "# R %d/%d\n", sector, count);
|
||||
fread(buff, 512, count, disk_image);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
static DRESULT simu_disk_write(BYTE lun, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
if (diskImage == 0) return RES_NOTRDY;
|
||||
traceDiskStatus();
|
||||
TRACE_SIMPGMSPACE("disk_ioctl(%u, %u, %p)", pdrv, cmd, buff);
|
||||
if (pdrv) return RES_PARERR;
|
||||
if (_seek(sector) != 0) return RES_ERROR;
|
||||
fprintf(stderr, "# W %d/%d\n", sector, count);
|
||||
fwrite(buff, 512, count, disk_image);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT res;
|
||||
BYTE *ptr = (BYTE *)buff;
|
||||
|
||||
if (cmd == CTRL_POWER) {
|
||||
switch (*ptr) {
|
||||
case 0: /* Sub control code == 0 (POWER_OFF) */
|
||||
res = RES_OK;
|
||||
break;
|
||||
case 1: /* Sub control code == 1 (POWER_ON) */
|
||||
res = RES_OK;
|
||||
break;
|
||||
case 2: /* Sub control code == 2 (POWER_GET) */
|
||||
*(ptr+1) = (BYTE)1; /* fake powered */
|
||||
res = RES_OK;
|
||||
break;
|
||||
default :
|
||||
res = RES_PARERR;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
static DRESULT simu_disk_ioctl(BYTE lun, BYTE cmd, void* buff)
|
||||
{
|
||||
if (!disk_image) return RES_NOTRDY;
|
||||
|
||||
DRESULT res = RES_OK;
|
||||
switch(cmd) {
|
||||
/* Generic command (Used by FatFs) */
|
||||
case CTRL_SYNC : /* Complete pending write process (needed at _FS_READONLY == 0) */
|
||||
break;
|
||||
|
||||
case GET_SECTOR_COUNT: /* Get media size (needed at _USE_MKFS == 1) */
|
||||
{
|
||||
struct stat buf;
|
||||
if (stat("sdcard.image", &buf) == 0) {
|
||||
DWORD noSectors = buf.st_size / 512;
|
||||
*(DWORD*)buff = noSectors;
|
||||
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_COUNT) = %u", noSectors);
|
||||
return RES_OK;
|
||||
}
|
||||
return RES_ERROR;
|
||||
case GET_SECTOR_COUNT:
|
||||
if (fseek(disk_image, 0, SEEK_END) == 0) {
|
||||
long sectors = ftell(disk_image) / 512L;
|
||||
fprintf(stderr, "# S %ld\n", sectors);
|
||||
*((DWORD*)buff) = (DWORD)(sectors);
|
||||
} else {
|
||||
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
|
||||
res = RES_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_SECTOR_SIZE: /* Get sector size (needed at _MAX_SS != _MIN_SS) */
|
||||
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_SIZE) = 512");
|
||||
case GET_SECTOR_SIZE:
|
||||
*(WORD*)buff = 512;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
case GET_BLOCK_SIZE : /* Get erase block size (needed at _USE_MKFS == 1) */
|
||||
case GET_BLOCK_SIZE:
|
||||
*(WORD*)buff = 512 * 4;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
case CTRL_TRIM : /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
|
||||
break;
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
case CTRL_LOCK : /* Lock/Unlock media removal */
|
||||
case CTRL_EJECT: /* Eject media */
|
||||
case CTRL_FORMAT: /* Create physical format on the media */
|
||||
return RES_PARERR;
|
||||
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
// case MMC_GET_TYPE 10 /* Get card type */
|
||||
// case MMC_GET_CSD 11 /* Get CSD */
|
||||
// case MMC_GET_CID 12 /* Get CID */
|
||||
// case MMC_GET_OCR 13 /* Get OCR */
|
||||
// case MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
// case ATA_GET_REV 20 /* Get F/W revision */
|
||||
// case ATA_GET_MODEL 21 /* Get model name */
|
||||
// case ATA_GET_SN 22 /* Get serial number */
|
||||
default:
|
||||
return RES_PARERR;
|
||||
break;
|
||||
}
|
||||
return RES_OK;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void sdInit(void)
|
||||
{
|
||||
// ioMutex = CoCreateMutex();
|
||||
// if (ioMutex >= CFG_MAX_MUTEX ) {
|
||||
// // sd error
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) {
|
||||
// call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called
|
||||
sdGetFreeSectors();
|
||||
|
||||
#if defined(LOG_TELEMETRY)
|
||||
f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE);
|
||||
if (f_size(&g_telemetryFile) > 0) {
|
||||
f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LOG_BLUETOOTH)
|
||||
f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE);
|
||||
if (f_size(&g_bluetoothFile) > 0) {
|
||||
f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
TRACE_SIMPGMSPACE("f_mount() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void sdDone()
|
||||
{
|
||||
if (sdMounted()) {
|
||||
audioQueue.stopSD();
|
||||
#if defined(LOG_TELEMETRY)
|
||||
f_close(&g_telemetryFile);
|
||||
#endif
|
||||
#if defined(LOG_BLUETOOTH)
|
||||
f_close(&g_bluetoothFile);
|
||||
#endif
|
||||
f_mount(NULL, "", 0); // unmount SD
|
||||
}
|
||||
}
|
||||
|
||||
void sdMount()
|
||||
{
|
||||
TRACE("sdMount");
|
||||
|
||||
diskCache.clear();
|
||||
|
||||
if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) {
|
||||
// call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called
|
||||
_g_FATFS_init = true;
|
||||
sdGetFreeSectors();
|
||||
|
||||
#if defined(LOG_TELEMETRY)
|
||||
f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE);
|
||||
if (f_size(&g_telemetryFile) > 0) {
|
||||
f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LOG_BLUETOOTH)
|
||||
f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE);
|
||||
if (f_size(&g_bluetoothFile) > 0) {
|
||||
f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
TRACE("f_mount() failed");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sdMounted()
|
||||
{
|
||||
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
|
||||
}
|
||||
|
||||
uint32_t sdIsHC()
|
||||
{
|
||||
return sdGetSize() > 2000000;
|
||||
}
|
||||
|
||||
uint32_t sdGetSpeed()
|
||||
{
|
||||
return 330000;
|
||||
}
|
||||
const diskio_driver_t simu_diskio_driver = {
|
||||
.initialize = simu_disk_initialize,
|
||||
.status = simu_disk_status,
|
||||
.read = simu_disk_read,
|
||||
.write = simu_disk_write,
|
||||
.ioctl = simu_disk_ioctl,
|
||||
};
|
||||
|
||||
#endif // #if defined(SIMU_DISKIO)
|
||||
|
||||
@@ -87,15 +87,14 @@ std::string fixPathDelimiters(const char * path)
|
||||
|
||||
void simuFatfsSetPaths(const char * sdPath, const char * settingsPath)
|
||||
{
|
||||
if (sdPath) {
|
||||
if (sdPath && strlen(sdPath) > 0) {
|
||||
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(sdPath));
|
||||
}
|
||||
else {
|
||||
} else if (simuSdDirectory.empty()) {
|
||||
char buff[1024];
|
||||
f_getcwd(buff, sizeof(buff)-1);
|
||||
f_getcwd(buff, sizeof(buff) - 1);
|
||||
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(buff));
|
||||
}
|
||||
if (settingsPath) {
|
||||
if (settingsPath && strlen(settingsPath) > 0) {
|
||||
simuSettingsDirectory = removeTrailingPathDelimiter(fixPathDelimiters(settingsPath));
|
||||
}
|
||||
TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSdDirectory: \"%s\"", simuSdDirectory.c_str());
|
||||
|
||||
@@ -57,51 +57,7 @@ static pixel_t _LCD_BUF2[DISPLAY_BUFFER_SIZE] __SDRAM;
|
||||
|
||||
pixel_t* simuLcdBuf = _LCD_BUF1;
|
||||
pixel_t* simuLcdBackBuf = _LCD_BUF2;
|
||||
#if 0
|
||||
// Copy 2 pixels at once to speed up a little
|
||||
static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_area)
|
||||
{
|
||||
coord_t x1 = LCD_W - copy_area.w - copy_area.x;
|
||||
coord_t y1 = LCD_H - copy_area.h - copy_area.y;
|
||||
|
||||
auto total = copy_area.w * copy_area.h;
|
||||
uint16_t* px_src = src + total - 2;
|
||||
|
||||
auto px_dst = dst + y1 * LCD_W + x1;
|
||||
for (auto line = 0; line < copy_area.h; line++) {
|
||||
|
||||
auto line_end = px_dst + (copy_area.w & ~1);
|
||||
while (px_dst != line_end) {
|
||||
uint32_t* px2_src = (uint32_t*)px_src;
|
||||
uint32_t* px2_dst = (uint32_t*)px_dst;
|
||||
|
||||
uint32_t px = ((*px2_src & 0xFFFF0000) >> 16) | ((*px2_src & 0xFFFF) << 16);
|
||||
*px2_dst = px;
|
||||
|
||||
px_src -= 2;
|
||||
px_dst += 2;
|
||||
}
|
||||
|
||||
if (copy_area.w & 1) {
|
||||
*(px_dst++) = *(px_src+1);
|
||||
px_src--;
|
||||
}
|
||||
|
||||
px_dst += LCD_W - copy_area.w;
|
||||
}
|
||||
}
|
||||
|
||||
static void _rotate_area_180(lv_area_t& area)
|
||||
{
|
||||
lv_coord_t tmp_coord;
|
||||
tmp_coord = area.y2;
|
||||
area.y2 = LCD_H - area.y1 - 1;
|
||||
area.y1 = LCD_H - tmp_coord - 1;
|
||||
tmp_coord = area.x2;
|
||||
area.x2 = LCD_W - area.x1 - 1;
|
||||
area.x1 = LCD_W - tmp_coord - 1;
|
||||
}
|
||||
#endif
|
||||
static void _copy_screen_area(uint16_t* dst, uint16_t* src, const lv_area_t& copy_area)
|
||||
{
|
||||
lv_coord_t x1 = copy_area.x1;
|
||||
@@ -150,8 +106,6 @@ static void simuRefreshLcd(lv_disp_drv_t * disp_drv, uint16_t *buffer, const rec
|
||||
simuLcdRefresh = true;
|
||||
|
||||
#else
|
||||
// // copy / rotate current area
|
||||
// _copy_rotate_180(simuLcdBackBuf, buffer, copy_area);
|
||||
_copy_area(simuLcdBackBuf, buffer, copy_area);
|
||||
|
||||
if (lv_disp_flush_is_last(disp_drv)) {
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
#include "hal/switch_driver.h"
|
||||
#include "definitions.h"
|
||||
#include "myeeprom.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "switches.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct hw_switch_def {
|
||||
const char* name;
|
||||
SwitchHwType type;
|
||||
@@ -34,7 +35,7 @@ struct hw_switch_def {
|
||||
|
||||
#include "simu_switches.inc"
|
||||
|
||||
int8_t switchesStates[MAX_SWITCHES] = { -1 };
|
||||
int8_t switchesStates[MAX_SWITCHES] = { 0 };
|
||||
|
||||
void simuSetSwitch(uint8_t swtch, int8_t state)
|
||||
{
|
||||
@@ -42,7 +43,9 @@ void simuSetSwitch(uint8_t swtch, int8_t state)
|
||||
switchesStates[swtch] = state;
|
||||
}
|
||||
|
||||
void boardInitSwitches() {}
|
||||
void boardInitSwitches() {
|
||||
memset(switchesStates, -1, sizeof(switchesStates));
|
||||
}
|
||||
|
||||
static uint8_t get_switch_index(uint8_t cat, uint8_t idx)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "widgets.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define _PI_ 3.14159265358979323846f
|
||||
|
||||
static inline float _clamp(float f)
|
||||
{
|
||||
return (f < 0.0) ? 0.0 : (f > 1.0) ? 1.0 : f;
|
||||
}
|
||||
|
||||
static inline void adjust_gimbal(float& pos)
|
||||
{
|
||||
if (pos != 0.5f) {
|
||||
auto diff = 0.5 - pos;
|
||||
if (std::abs(diff) > 0.01) {
|
||||
pos += diff / 10;
|
||||
} else {
|
||||
pos = 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_tick(const ImVec2& center, ImU32 col, float angle,
|
||||
float start, float end, float thickness)
|
||||
{
|
||||
auto angle_cos = std::cos(angle);
|
||||
auto angle_sin = std::sin(angle);
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddLine({center.x + angle_cos * end, center.y + angle_sin * end},
|
||||
{center.x + angle_cos * start, center.y + angle_sin * start},
|
||||
col, thickness);
|
||||
}
|
||||
|
||||
static void draw_gimbal_frame(const ImVec2& p0, const ImVec2& p1, float rounding, float thickness)
|
||||
{
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
auto bg_col = ImGui::GetColorU32(ImGuiCol_FrameBg);
|
||||
draw_list->AddRectFilled(p0, p1, bg_col, rounding,
|
||||
ImDrawFlags_RoundCornersAll);
|
||||
|
||||
auto frame_col = ImGui::GetColorU32(ImGuiCol_Border);
|
||||
draw_list->AddRect(p0, p1, frame_col, rounding,
|
||||
ImDrawFlags_RoundCornersAll, thickness);
|
||||
|
||||
float gimbal_mid = (p1.x - p0.x) / 2.0;
|
||||
float circle_radius = gimbal_mid * 0.75;
|
||||
ImVec2 center = {p0.x + gimbal_mid, p0.y + gimbal_mid};
|
||||
|
||||
float circle_thickness = thickness * 1.8f;
|
||||
float inner_radius = circle_radius - circle_thickness / 2.0;
|
||||
|
||||
draw_list->AddCircle(center, circle_radius, frame_col, 0, thickness * 1.8f);
|
||||
|
||||
for (float deg = 0.0f; deg < 360.0f; deg += 10.0f) {
|
||||
float angle = _PI_ * deg / 180.0f;
|
||||
draw_tick(center, IM_COL32_WHITE, angle,
|
||||
circle_radius - circle_thickness * 0.3f,
|
||||
circle_radius + circle_thickness * 0.3f,
|
||||
circle_thickness * 0.2f);
|
||||
}
|
||||
|
||||
for (float deg = 0.0f; deg < 360.0f; deg += 90.0f) {
|
||||
float angle = _PI_ * deg / 180.0f;
|
||||
draw_tick(center, frame_col, angle,
|
||||
inner_radius, circle_thickness, circle_thickness * 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
void SingleGimbal(const char* name, GimbalState& gs)
|
||||
{
|
||||
const auto gimbal_width = 120.0f;
|
||||
const auto border_rounding = 12.0f;
|
||||
const auto border_thickness = 6.0f;
|
||||
const auto default_dot_radius = 12.0f;
|
||||
|
||||
ImGui::InvisibleButton(name, ImVec2(gimbal_width, gimbal_width));
|
||||
|
||||
auto p0 = ImGui::GetItemRectMin();
|
||||
p0.x += 6.0f;
|
||||
p0.y += 6.0f;
|
||||
|
||||
auto p1 = ImGui::GetItemRectMax();
|
||||
p1.x -= 6.0f;
|
||||
p1.y -= 6.0f;
|
||||
|
||||
draw_gimbal_frame(p0, p1, border_rounding, border_thickness);
|
||||
|
||||
float dot_radius = default_dot_radius;
|
||||
ImU32 col = ImGui::GetColorU32(ImGuiCol_Button);
|
||||
if (ImGui::IsItemActive()) {
|
||||
dot_radius += 1.5;
|
||||
col = ImGui::GetColorU32(ImGuiCol_ButtonActive);
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
col = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
|
||||
}
|
||||
|
||||
float eff_width = gimbal_width - 12.0f - 2.0 * default_dot_radius;
|
||||
float half_width = (gimbal_width - 12.0f) / 2.0;
|
||||
|
||||
if (ImGui::IsItemActive()) {
|
||||
const auto& io = ImGui::GetIO();
|
||||
ImVec2 mouse_pos(io.MousePos.x - p0.x, io.MousePos.y - p0.y);
|
||||
gs.pos.x = _clamp((mouse_pos.x - half_width) / eff_width + 0.5);
|
||||
gs.pos.y = _clamp((mouse_pos.y - half_width) / eff_width + 0.5);
|
||||
} else {
|
||||
adjust_gimbal(gs.pos.x);
|
||||
if (!gs.lock_y) adjust_gimbal(gs.pos.y);
|
||||
}
|
||||
|
||||
ImVec2 dot_center(p0.x + eff_width * (gs.pos.x - 0.5) + half_width,
|
||||
p0.y + eff_width * (gs.pos.y - 0.5) + half_width);
|
||||
|
||||
// Make color opaque
|
||||
col |= IM_COL32_BLACK;
|
||||
ImGui::GetWindowDrawList()->AddCircle(dot_center, dot_radius, col, 0, dot_radius - 1.0);
|
||||
}
|
||||
|
||||
void GimbalPair(const char* str_id, GimbalState& left, GimbalState& right)
|
||||
{
|
||||
ImGui::PushID(str_id);
|
||||
ImGui::BeginGroup();
|
||||
|
||||
SingleGimbal("#g-left", left);
|
||||
ImGui::SameLine();
|
||||
SingleGimbal("#g-right", right);
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void SimuScreen(const ScreenDesc& desc, ImTextureID screen_img, ImVec2 size,
|
||||
ImU32 bg_col, ImU32 overlay_col)
|
||||
{
|
||||
ImGui::InvisibleButton("#simu-screen", size);
|
||||
if (!ImGui::IsItemVisible()) return;
|
||||
|
||||
ImVec2 p_min = ImGui::GetItemRectMin();
|
||||
ImVec2 p_max = ImGui::GetItemRectMax();
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
if (bg_col != IM_COL32_BLACK_TRANS) {
|
||||
draw_list->AddRectFilled(p_min, p_max, bg_col);
|
||||
}
|
||||
draw_list->AddImage(screen_img, p_min, p_max);
|
||||
|
||||
if (desc.is_dot_matrix) {
|
||||
float dx = size.x / float(desc.width);
|
||||
float thickness = dx / 50.0;
|
||||
auto col = bg_col & 0x80FFFFFF;
|
||||
for (int x = 1; x < desc.width; x++) {
|
||||
ImVec2 p1 = { p_min.x + x * size.x / float(desc.width), p_min.y };
|
||||
ImVec2 p2 = { p1.x, p_min.y + size.y - 1 };
|
||||
draw_list->AddLine(p1, p2, col, thickness);
|
||||
}
|
||||
for (int y = 1; y < desc.height; y++) {
|
||||
ImVec2 p1 = { p_min.x, p_min.y + y * size.y / float(desc.height) };
|
||||
ImVec2 p2 = { p_min.x + size.x - 1, p1.y };
|
||||
draw_list->AddLine(p1, p2, col, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
if (overlay_col != IM_COL32_BLACK_TRANS) {
|
||||
draw_list->AddRectFilled(p_min, p_max, overlay_col);
|
||||
}
|
||||
}
|
||||
|
||||
bool SimuScreenMouseEvent(const ScreenDesc& desc, ScreenMouseEvent& event)
|
||||
{
|
||||
bool active = ImGui::IsItemActive() && ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
bool deactivated = ImGui::IsItemDeactivated() && ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
||||
|
||||
if (active || deactivated) {
|
||||
|
||||
auto size = ImGui::GetItemRectSize();
|
||||
float scale_x = float(desc.width) / size.x;
|
||||
float scale_y = float(desc.height) / size.y;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
auto p0 = ImGui::GetItemRectMin();
|
||||
|
||||
const ImVec2 mouse_pos(io.MousePos.x - p0.x, io.MousePos.y - p0.y);
|
||||
const ImVec2 scaled_pos(mouse_pos.x * scale_x, mouse_pos.y * scale_y);
|
||||
|
||||
if (active) {
|
||||
event.type = ScreenMouseEventType::MouseDown;
|
||||
} else {
|
||||
event.type = ScreenMouseEventType::MouseUp;
|
||||
}
|
||||
|
||||
event.pos_x = (int)std::round(scaled_pos.x);
|
||||
event.pos_y = (int)std::round(scaled_pos.y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
struct GimbalState {
|
||||
ImVec2 pos;
|
||||
bool lock_y;
|
||||
};
|
||||
|
||||
struct ScreenDesc {
|
||||
int width;
|
||||
int height;
|
||||
bool is_dot_matrix;
|
||||
};
|
||||
|
||||
enum class ScreenMouseEventType {
|
||||
MouseDown,
|
||||
MouseUp,
|
||||
};
|
||||
|
||||
struct ScreenMouseEvent {
|
||||
ScreenMouseEventType type;
|
||||
int pos_x;
|
||||
int pos_y;
|
||||
};
|
||||
|
||||
void GimbalPair(const char* str_id, GimbalState& left, GimbalState& right);
|
||||
void SimuScreen(const ScreenDesc& desc, ImTextureID screen_img, ImVec2 size,
|
||||
ImU32 bg_col, ImU32 overlay_col);
|
||||
bool SimuScreenMouseEvent(const ScreenDesc& desc, ScreenMouseEvent& event);
|
||||
@@ -31,7 +31,7 @@
|
||||
void backlightEnable(uint8_t level) {}
|
||||
void backlightFullOn() {}
|
||||
void backlightDisable() {}
|
||||
uint8_t isBacklightEnabled() {return false;}
|
||||
bool isBacklightEnabled() { return false; }
|
||||
#elif defined(PCBX9E)
|
||||
void backlightInit()
|
||||
{
|
||||
@@ -65,7 +65,7 @@ void backlightDisable()
|
||||
BACKLIGHT_TIMER->CCR2 = 0;
|
||||
}
|
||||
|
||||
uint8_t isBacklightEnabled()
|
||||
bool isBacklightEnabled()
|
||||
{
|
||||
return (BACKLIGHT_TIMER->CCR1 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ void backlightDisable()
|
||||
BACKLIGHT_TIMER->CCR2 = 0;
|
||||
}
|
||||
|
||||
uint8_t isBacklightEnabled()
|
||||
bool isBacklightEnabled()
|
||||
{
|
||||
return (BACKLIGHT_TIMER->CCR4 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
|
||||
}
|
||||
@@ -144,7 +144,7 @@ void backlightDisable()
|
||||
BACKLIGHT_COUNTER_REGISTER = 0;
|
||||
}
|
||||
|
||||
uint8_t isBacklightEnabled()
|
||||
bool isBacklightEnabled()
|
||||
{
|
||||
return BACKLIGHT_COUNTER_REGISTER != 0;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ void backlightDisable()
|
||||
BACKLIGHT_TIMER->CCR1 = 0;
|
||||
}
|
||||
|
||||
uint8_t isBacklightEnabled()
|
||||
bool isBacklightEnabled()
|
||||
{
|
||||
return BACKLIGHT_TIMER->CCR1 != 0;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ void pwrResetHandler();
|
||||
void backlightInit();
|
||||
void backlightDisable();
|
||||
void backlightFullOn();
|
||||
uint8_t isBacklightEnabled();
|
||||
bool isBacklightEnabled();
|
||||
|
||||
#if defined(PCBX9E) || defined(PCBX9DP)
|
||||
void backlightEnable(uint8_t level, uint8_t color);
|
||||
|
||||
@@ -128,18 +128,6 @@ uint16_t simu_get_analog(uint8_t idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsLedRGB(uint8_t idx, uint32_t color)
|
||||
{
|
||||
}
|
||||
|
||||
void fsLedOn(uint8_t idx)
|
||||
{
|
||||
}
|
||||
|
||||
void fsLedOff(uint8_t idx)
|
||||
{
|
||||
}
|
||||
|
||||
static char _stringResult[200];
|
||||
|
||||
const char * nchar2string(const char * string, int size)
|
||||
|
||||
Vendored
+1
-1
@@ -333,7 +333,7 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#if defined(SIMU)
|
||||
#if defined(SIMU) && !defined(SIMU_DISKIO)
|
||||
UINT f_size(FIL* fil);
|
||||
#else
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
|
||||
Reference in New Issue
Block a user