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:
Raphael Coeffic
2025-07-20 11:30:05 +02:00
committed by GitHub
co-authored by raphaelcoeffic Malte Langermann
parent 14287cb91d
commit 4eddda034d
46 changed files with 2136 additions and 1577 deletions
+26
View File
@@ -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
)
-63
View File
@@ -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
)
-71
View File
@@ -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)
-68
View File
@@ -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
)
-54
View File
@@ -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
)
+1
View File
@@ -28,6 +28,7 @@ endif()
set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_C99=1) # proper to_string definition add_definitions(-D_GLIBCXX_USE_C99=1) # proper to_string definition
+17 -31
View File
@@ -1,41 +1,27 @@
option(DISABLE_COMPANION "Disable building companion and simulators" OFF) 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) if(NOT DISABLE_COMPANION)
include(QtDefs) include(QtDefs)
endif(NOT DISABLE_COMPANION) endif(NOT DISABLE_COMPANION)
if(Qt5Core_FOUND OR FOX_FOUND) # this prevents FindSDL from appending SDLmain lib to the results, which we don't want
set(SDL2_BUILDING_LIBRARY YES) # this prevents FindSDL from appending SDLmain lib to the results, which we don't want set(SDL2_BUILDING_LIBRARY YES)
find_package("SDL2") 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 if(SDL2_FOUND)
find_file(SDL2_LIB_PATH # find_package("SDL2") does not set a variable holding the path to the location of the SDL2 shared library
NAMES find_file(SDL2_LIB_PATH
libSDL2.so NAMES
SDL2.dll libSDL2.so
SDL2.dylib SDL2.dll
HINTS SDL2.dylib
"/usr/lib/x86_64-linux-gnu" HINTS
${SDL2_LIBRARY_PATH}) "/usr/lib/x86_64-linux-gnu"
message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}") ${SDL2_LIBRARY_PATH})
else() message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}")
message(STATUS "SDL2 not found! Simulator audio, and joystick inputs, will not work.") else()
endif() message(STATUS "SDL not found! Simulator audio, and joystick inputs, will not work.")
endif() endif()
if(Qt5Core_FOUND AND NOT DISABLE_COMPANION) if(Qt5Core_FOUND AND NOT DISABLE_COMPANION)
+8 -8
View File
@@ -6,14 +6,14 @@ endif()
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
find_package(Qt5Core) find_package(Qt5Core QUIET)
find_package(Qt5Widgets) find_package(Qt5Widgets QUIET)
find_package(Qt5Xml) find_package(Qt5Xml QUIET)
find_package(Qt5LinguistTools) find_package(Qt5LinguistTools QUIET)
find_package(Qt5PrintSupport) find_package(Qt5PrintSupport QUIET)
find_package(Qt5Multimedia) find_package(Qt5Multimedia QUIET)
find_package(Qt5Svg) find_package(Qt5Svg QUIET)
find_package(Qt5SerialPort) find_package(Qt5SerialPort QUIET)
if(Qt5Core_FOUND) if(Qt5Core_FOUND)
message(STATUS "Qt Version: ${Qt5Core_VERSION}") message(STATUS "Qt Version: ${Qt5Core_VERSION}")
-11
View File
@@ -28,9 +28,6 @@ option(SEMIHOSTING "Enable debugger semihosting" OFF)
option(JITTER_MEASURE "Enable ADC jitter measurement" OFF) option(JITTER_MEASURE "Enable ADC jitter measurement" OFF)
option(WATCHDOG "Enable hardware Watchdog" ON) option(WATCHDOG "Enable hardware Watchdog" ON)
option(ASTERISK "Enable asterisk icon (test only firmware)" OFF) 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(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_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) 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) add_definitions(-DUSE_WATCHDOG)
endif() endif()
if(SIMU_AUDIO)
add_definitions(-DSIMU_AUDIO)
endif()
if(SIMU_DISKIO) if(SIMU_DISKIO)
add_definitions(-DSIMU_DISKIO) add_definitions(-DSIMU_DISKIO)
endif() endif()
@@ -493,10 +486,6 @@ endif()
if(NATIVE_BUILD) if(NATIVE_BUILD)
set(RADIOLIB_NATIVE_SRC ${SRC}) 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 # Pack all radio sources into an object lib
add_library(radiolib_native OBJECT EXCLUDE_FROM_ALL add_library(radiolib_native OBJECT EXCLUDE_FROM_ALL
+24 -12
View File
@@ -370,6 +370,17 @@ AudioQueue::AudioQueue()
#define CODEC_ID_PCM_S16LE 1 #define CODEC_ID_PCM_S16LE 1
static void _audio_lock()
{
mutex_lock(&audioMutex);
}
static void _audio_unlock()
{
mutex_unlock(&audioMutex);
}
#if !defined(__SSAT) #if !defined(__SSAT)
#define _sat_s16(x) ((int16_t)limit<int32_t>(INT16_MIN, (x), INT16_MAX)) #define _sat_s16(x) ((int16_t)limit<int32_t>(INT16_MIN, (x), INT16_MAX))
#else #else
@@ -613,9 +624,9 @@ void AudioQueue::wakeup()
// mix the normal context (tones and wavs) // mix the normal context (tones and wavs)
if (normalContext.isEmpty() && !fragmentsFifo.empty()) { if (normalContext.isEmpty() && !fragmentsFifo.empty()) {
mutex_lock(&audioMutex); _audio_lock();
normalContext.setFragment(fragmentsFifo.get()); normalContext.setFragment(fragmentsFifo.get());
mutex_unlock(&audioMutex); _audio_unlock();
} }
result = normalContext.mixBuffer(buffer, g_eeGeneral.beepVolume, g_eeGeneral.wavVolume, fade); result = normalContext.mixBuffer(buffer, g_eeGeneral.beepVolume, g_eeGeneral.wavVolume, fade);
if (result > 0) { 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) 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) #if defined(SIMU) && !defined(SIMU_AUDIO)
#warning "NO audio"
return; return;
#endif #endif
mutex_lock(&audioMutex); _audio_lock();
freq = limit<uint16_t>(BEEP_MIN_FREQ, freq, BEEP_MAX_FREQ); 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) 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; return;
} }
mutex_lock(&audioMutex); _audio_lock();
if (flags & PLAY_BACKGROUND) { if (flags & PLAY_BACKGROUND) {
backgroundContext.clear(); 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)); fragmentsFifo.push(AudioFragment(filename, flags & 0x0f, fragmentVolume, id));
} }
mutex_unlock(&audioMutex); _audio_unlock();
} }
void AudioQueue::stopPlay(uint8_t id) void AudioQueue::stopPlay(uint8_t id)
@@ -768,12 +780,12 @@ void AudioQueue::stopPlay(uint8_t id)
return; return;
#endif #endif
mutex_lock(&audioMutex); _audio_lock();
fragmentsFifo.removePromptById(id); fragmentsFifo.removePromptById(id);
backgroundContext.stop(id); backgroundContext.stop(id);
mutex_unlock(&audioMutex); _audio_unlock();
} }
void AudioQueue::stopSD() void AudioQueue::stopSD()
@@ -786,19 +798,19 @@ void AudioQueue::stopSD()
void AudioQueue::stopAll() void AudioQueue::stopAll()
{ {
flush(); flush();
mutex_lock(&audioMutex); _audio_lock();
priorityContext.clear(); priorityContext.clear();
normalContext.clear(); normalContext.clear();
mutex_unlock(&audioMutex); _audio_unlock();
} }
void AudioQueue::flush() void AudioQueue::flush()
{ {
mutex_lock(&audioMutex); _audio_lock();
fragmentsFifo.clear(); fragmentsFifo.clear();
varioContext.clear(); varioContext.clear();
backgroundContext.clear(); backgroundContext.clear();
mutex_unlock(&audioMutex); _audio_unlock();
} }
void audioPlay(unsigned int index, uint8_t id) void audioPlay(unsigned int index, uint8_t id)
-1
View File
@@ -469,7 +469,6 @@ enum {
void codecsInit(); void codecsInit();
void audioEvent(unsigned int index); void audioEvent(unsigned int index);
void audioPlay(unsigned int index, uint8_t id=0); void audioPlay(unsigned int index, uint8_t id=0);
void audioStart();
#if defined(AUDIO) && defined(BUZZER) #if defined(AUDIO) && defined(BUZZER)
#define AUDIO_BUZZER(a, b) do { a; b; } while(0) #define AUDIO_BUZZER(a, b) do { a; b; } while(0)
+1 -1
View File
@@ -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_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); lcdDrawText(FS_2ND_COLUMN + 5, y, getFSLogicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP);
#if defined(FUNCTION_SWITCHES_RGB_LEDS) #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 #else
lcdDrawText(FS_3RD_COLUMN, y, STR_OFFON[fsLedState(i)]); lcdDrawText(FS_3RD_COLUMN, y, STR_OFFON[fsLedState(i)]);
#endif #endif
+1 -5
View File
@@ -102,11 +102,7 @@ void waitSplash()
{ {
// Handle color splash screen // Handle color splash screen
if (splashStartTime) { if (splashStartTime) {
#if defined(SIMU) inactivityCheckInputs();
// Simulator - inputsMoved() returns true immediately without this!
sleep_ms(30);
#endif // defined(SIMU)
splashStartTime += SPLASH_TIMEOUT; splashStartTime += SPLASH_TIMEOUT;
while (splashStartTime >= get_tmr10ms()) { while (splashStartTime >= get_tmr10ms()) {
LvglWrapper::instance()->run(); LvglWrapper::instance()->run();
+1 -1
View File
@@ -30,7 +30,7 @@ set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
${CMAKE_CURRENT_SOURCE_DIR}/hal/key_driver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hal/key_driver.cpp
) )
if (NOT NATIVE_BUILD) if ((NOT NATIVE_BUILD) OR SIMU_DISKIO)
set(SRC ${SRC} set(SRC ${SRC}
hal/storage.cpp hal/storage.cpp
hal/fatfs_diskio.cpp hal/fatfs_diskio.cpp
+4 -1
View File
@@ -24,7 +24,10 @@
#include "debug.h" #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" #include "diskio_sdio.h"
#define _STORAGE_DRIVER sdio_diskio_driver #define _STORAGE_DRIVER sdio_diskio_driver
#elif defined(STORAGE_USE_SDCARD_SPI) #elif defined(STORAGE_USE_SDCARD_SPI)
+6 -1
View File
@@ -52,6 +52,11 @@ void inactivityTimerReset(ActivitySource src)
#define INAC_STICKS_SHIFT 7 #define INAC_STICKS_SHIFT 7
#define INAC_SWITCHES_SHIFT 8 #define INAC_SWITCHES_SHIFT 8
static inline int8_t inactivity_diff(uint8_t sum)
{
return abs((int8_t)(sum - inactivity.sum));
}
bool inactivityCheckInputs() bool inactivityCheckInputs()
{ {
uint8_t sum = 0; uint8_t sum = 0;
@@ -78,7 +83,7 @@ bool inactivityCheckInputs()
sum += get_spacemouse_value(i) >> INAC_STICKS_SHIFT; sum += get_spacemouse_value(i) >> INAC_STICKS_SHIFT;
#endif #endif
if (abs((int8_t)(sum - inactivity.sum)) > 1) { if (inactivity_diff(sum) > 1) {
inactivity.sum = sum; inactivity.sum = sum;
return true; return true;
} else { } else {
+1 -1
View File
@@ -109,7 +109,7 @@ FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime)
return res; return res;
} }
#if !defined(BOOT) && !defined(SIMU) #if !defined(BOOT) && (!defined(SIMU) || defined(SIMU_DISKIO))
// Replace FatFS implementation of f_puts and f_printf // Replace FatFS implementation of f_puts and f_printf
+2
View File
@@ -4,6 +4,8 @@
#include <algorithm> #include <algorithm>
using namespace std::chrono_literals;
static timer_queue* _instance = nullptr; static timer_queue* _instance = nullptr;
static std::mutex _instance_mut; static std::mutex _instance_mut;
+1 -1
View File
@@ -565,7 +565,7 @@ void sdDone()
uint32_t sdMounted() uint32_t sdMounted()
{ {
#if defined(SIMU) #if defined(SIMU) && !defined(SIMU_DISKIO)
return true; return true;
#else #else
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0); return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
-727
View File
@@ -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);
}
+3 -1
View File
@@ -21,8 +21,10 @@
#pragma once #pragma once
#include <cinttypes> #include <stdint.h>
#include <stdlib.h>
#include "edgetx_types.h"
#include "edgetx_constants.h" #include "edgetx_constants.h"
enum LogicalSwitchFamilies { enum LogicalSwitchFamilies {
+41 -47
View File
@@ -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) if(NOT SIMU_TARGET)
message(STATUS "libsimulator/simu targets disabled") message(STATUS "libsimulator/simu targets disabled")
return() return()
endif() 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 set(SIMU_DRIVERS
simpgmspace.cpp simpgmspace.cpp
simufatfs.cpp simufatfs.cpp
simudisk.cpp simudisk.cpp
simulcd.cpp simulcd.cpp
simuaudio.cpp
switch_driver.cpp switch_driver.cpp
adc_driver.cpp adc_driver.cpp
module_drivers.cpp module_drivers.cpp
@@ -156,60 +166,44 @@ if(WIN32)
endif() endif()
endif(WIN32) endif(WIN32)
if(MSVC) if(MINGW)
set(CMAKE_CXX_FLAGS "/EHsc") # 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
if(NOT CLANG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /LD /MP") endif()
endif()
else() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
# 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") if(ASAN)
endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
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()
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_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) include(FetchImgui)
add_executable(simu WIN32 target_compile_options(simu PUBLIC -DSIMU)
EXCLUDE_FROM_ALL
${SIMU_SRC}
${RADIO_SRC_DIR}/simu.cpp)
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} ) add_png_target(image_assets "assets/images/*.png")
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL2_LIBRARIES}) add_dependencies(simu image_assets)
target_compile_options(simu PRIVATE ${SIMU_SRC_OPTIONS})
endif()
if(APPLE) set(STB_DIR ${RADIO_SRC_DIR}/thirdparty/stb)
# OS X compiler no longer automatically includes /Library/Frameworks in search path target_include_directories(simu PUBLIC ${STB_DIR})
set(CMAKE_SHARED_LINKER_FLAGS -F/Library/Frameworks) target_link_libraries(simu PUBLIC imgui)
set(SIMULATOR_BUNDLES) target_link_libraries(simu PUBLIC
foreach(library ${OPENTX_LIBRARIES}) pthread
set(SIMULATOR_BUNDLE "${library}-bundle") ${SDL2_LIBRARIES}
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)
PrintTargetReport("simu/libsimulator") PrintTargetReport("simu/libsimulator")
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

+28 -4
View File
@@ -19,11 +19,35 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include "board.h"
bool boardBacklightOn = false; bool boardBacklightOn = false;
bool isBacklightEnabled() { return boardBacklightOn; } bool isBacklightEnabled() { return boardBacklightOn; }
void backlightFullOn() { boardBacklightOn = true; }
void backlightInit() {} void backlightInit() {}
void backlightEnable(unsigned char) {}
void backlightEnable(unsigned char, unsigned char) {} #if !defined(COLORLCD)
void backlightDisable() {}
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
+139
View File
@@ -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();
}
}
+25
View File
@@ -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);
+4 -3
View File
@@ -19,6 +19,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
void gyroInit() {} #include "gyro.h"
void gyroRead() {}
void gyroRead(unsigned char*) {} int gyroInit() { return -1; }
int gyroRead(uint8_t*) { return -1; }
+259
View File
@@ -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
+57
View File
@@ -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
+33 -16
View File
@@ -23,13 +23,15 @@
#include "hal/rgbleds.h" #include "hal/rgbleds.h"
#include "definitions.h" #include "definitions.h"
#include "board.h"
bool usbChargerLed() { return true; } bool usbChargerLed() { return true; }
void ledRed() {} void ledRed() {}
void ledGreen() {} void ledGreen() {}
void ledBlue() {} void ledBlue() {}
void ledOff() {} 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() {} void rgbLedColorApply() {}
uint8_t getRGBColorIndex(uint32_t color) uint8_t getRGBColorIndex(uint32_t color)
@@ -41,24 +43,39 @@ uint8_t getRGBColorIndex(uint32_t color)
return 0; // Custom value set with Companion return 0; // Custom value set with Companion
} }
// RGB #if NUM_FUNCTIONS_SWITCHES > 0
#define WS2812_BYTES_PER_LED 3 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 void fsLedRGB(uint8_t index, uint32_t col)
#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)
{ {
uint8_t* pixel = &_led_colors[led * WS2812_BYTES_PER_LED]; _fs_switch_color_mask |= (1 << index);
return (pixel[1] << 16) + (pixel[0] << 8) + pixel[2]; _fs_switch_colors[index] = col;
} }
uint32_t fsGetLedRGB(uint8_t index) 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
+3 -13
View File
@@ -22,6 +22,7 @@
#include "opentxsimulator.h" #include "opentxsimulator.h"
#include "edgetx.h" #include "edgetx.h"
#include "simulcd.h" #include "simulcd.h"
#include "simuaudio.h"
#include "switches.h" #include "switches.h"
#include "serial.h" #include "serial.h"
#include "myeeprom.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 // Serial port handling needs to know about OpenTxSimulator, so we we
// need to update what's in simpgmspace.cpp when we have a simulator // need to update what's in simpgmspace.cpp when we have a simulator
// to point at. // to point at.
@@ -283,7 +272,7 @@ void OpenTxSimulator::start(const char * filename, bool tests)
QMutexLocker lckr(&m_mtxSimuMain); QMutexLocker lckr(&m_mtxSimuMain);
QMutexLocker slckr(&m_mtxSettings); QMutexLocker slckr(&m_mtxSettings);
startAudio(volumeGain); simuAudioInit();
simuStart(tests, simuSdDirectory.toLatin1().constData(), simuStart(tests, simuSdDirectory.toLatin1().constData(),
simuSettingsDirectory.toLatin1().constData()); simuSettingsDirectory.toLatin1().constData());
@@ -301,6 +290,7 @@ void OpenTxSimulator::stop()
QMutexLocker lckr(&m_mtxSimuMain); QMutexLocker lckr(&m_mtxSimuMain);
simuStop(); simuStop();
simuAudioDeInit();
emit stopped(); emit stopped();
} }
+908
View File
@@ -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;
}
+22 -128
View File
@@ -127,6 +127,8 @@ void simuInit()
// Init ADC driver callback // Init ADC driver callback
adcInit(&simu_adc_driver); adcInit(&simu_adc_driver);
// Switches
switchInit();
} }
bool keysStates[MAX_KEYS] = { false }; bool keysStates[MAX_KEYS] = { false };
@@ -205,12 +207,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
} }
#endif #endif
#if defined(SIMU_EXCEPTIONS)
signal(SIGFPE, sig);
signal(SIGSEGV, sig);
try {
#endif
// Init LCD callbacks // Init LCD callbacks
lcdInit(); lcdInit();
@@ -228,12 +224,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
#endif #endif
simu_running = true; simu_running = true;
#if defined(SIMU_EXCEPTIONS)
}
catch (...) {
}
#endif
} }
extern task_handle_t mixerTaskId; extern task_handle_t mixerTaskId;
@@ -250,130 +240,14 @@ void simuStop()
simu_shutdown = true; simu_shutdown = true;
task_shutdown_all(); task_shutdown_all();
#if defined(SIMU_AUDIO)
stopAudio();
#endif
simu_running = false; simu_running = false;
} }
struct SimulatorAudio {
int volumeGain;
int currentVolume;
int16_t leftoverData[AUDIO_BUFFER_SIZE];
int leftoverLen;
} simuAudio;
bool simuIsRunning() bool simuIsRunning()
{ {
return simu_running; 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) #if !defined(COLORLCD)
void lcdSetRefVolt(uint8_t val) void lcdSetRefVolt(uint8_t val)
{ {
@@ -617,6 +491,12 @@ const etx_serial_port_t* auxSerialGetPort(int port_nr)
struct TouchState simTouchState = {}; struct TouchState simTouchState = {};
bool simTouchOccured = false; bool simTouchOccured = false;
bool touchPanelInit()
{
simTouchState.x = simTouchState.y = 0;
return true;
}
bool touchPanelEventOccured() bool touchPanelEventOccured()
{ {
if(simTouchOccured) if(simTouchOccured)
@@ -627,6 +507,20 @@ bool touchPanelEventOccured()
return false; 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 touchPanelRead()
{ {
struct TouchState st = simTouchState; struct TouchState st = simTouchState;
+38
View File
@@ -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();
+103
View File
@@ -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;
}
}
+25
View File
@@ -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();
+63 -233
View File
@@ -20,272 +20,102 @@
*/ */
#if defined(SIMU_DISKIO) #if defined(SIMU_DISKIO)
#include "edgetx.h" #include "hal/fatfs_diskio.h"
#include "ff.h"
#include "diskio.h"
#include <time.h>
#include <stdio.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); if (!disk_image) {
return 1; fprintf(stderr, "disk image is NULL\n");
} 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 (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(); if (!image_path) return STA_NODISK;
TRACE_SIMPGMSPACE("disk_initialize(%u)", pdrv); disk_image = fopen(image_path, "rb+");
diskImage = fopen("sdcard.image", "rb+"); if (!disk_image) {
return diskImage ? (DSTATUS)0 : (DSTATUS)STA_NODISK; 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; 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; if (_seek(sector) != 0) return RES_ERROR;
traceDiskStatus(); fprintf(stderr, "# R %d/%d\n", sector, count);
TRACE_SIMPGMSPACE("disk_write(%u, %p, %u, %u)", pdrv, buff, sector, count); fread(buff, 512, count, disk_image);
fseek(diskImage, sector*512, SEEK_SET);
fwrite(buff, count, 512, diskImage);
return RES_OK; 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; if (_seek(sector) != 0) return RES_ERROR;
traceDiskStatus(); fprintf(stderr, "# W %d/%d\n", sector, count);
TRACE_SIMPGMSPACE("disk_ioctl(%u, %u, %p)", pdrv, cmd, buff); fwrite(buff, 512, count, disk_image);
if (pdrv) return RES_PARERR; return RES_OK;
}
DRESULT res; static DRESULT simu_disk_ioctl(BYTE lun, BYTE cmd, void* buff)
BYTE *ptr = (BYTE *)buff; {
if (!disk_image) return RES_NOTRDY;
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;
}
DRESULT res = RES_OK;
switch(cmd) { switch(cmd) {
/* Generic command (Used by FatFs) */ case GET_SECTOR_COUNT:
case CTRL_SYNC : /* Complete pending write process (needed at _FS_READONLY == 0) */ if (fseek(disk_image, 0, SEEK_END) == 0) {
break; long sectors = ftell(disk_image) / 512L;
fprintf(stderr, "# S %ld\n", sectors);
case GET_SECTOR_COUNT: /* Get media size (needed at _USE_MKFS == 1) */ *((DWORD*)buff) = (DWORD)(sectors);
{ } else {
struct stat buf; fprintf(stderr, "fseek failed: %s\n", strerror(errno));
if (stat("sdcard.image", &buf) == 0) { res = RES_ERROR;
DWORD noSectors = buf.st_size / 512;
*(DWORD*)buff = noSectors;
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_COUNT) = %u", noSectors);
return RES_OK;
}
return RES_ERROR;
} }
break;
case GET_SECTOR_SIZE: /* Get sector size (needed at _MAX_SS != _MIN_SS) */ case GET_SECTOR_SIZE:
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_SIZE) = 512");
*(WORD*)buff = 512; *(WORD*)buff = 512;
res = RES_OK;
break; break;
case GET_BLOCK_SIZE : /* Get erase block size (needed at _USE_MKFS == 1) */ case GET_BLOCK_SIZE:
*(WORD*)buff = 512 * 4; *(WORD*)buff = 512 * 4;
res = RES_OK;
break; 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: default:
return RES_PARERR; break;
} }
return RES_OK;
return res;
} }
void sdInit(void) const diskio_driver_t simu_diskio_driver = {
{ .initialize = simu_disk_initialize,
// ioMutex = CoCreateMutex(); .status = simu_disk_status,
// if (ioMutex >= CFG_MAX_MUTEX ) { .read = simu_disk_read,
// // sd error .write = simu_disk_write,
// return; .ioctl = simu_disk_ioctl,
// } };
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;
}
#endif // #if defined(SIMU_DISKIO) #endif // #if defined(SIMU_DISKIO)
+4 -5
View File
@@ -87,15 +87,14 @@ std::string fixPathDelimiters(const char * path)
void simuFatfsSetPaths(const char * sdPath, const char * settingsPath) void simuFatfsSetPaths(const char * sdPath, const char * settingsPath)
{ {
if (sdPath) { if (sdPath && strlen(sdPath) > 0) {
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(sdPath)); simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(sdPath));
} } else if (simuSdDirectory.empty()) {
else {
char buff[1024]; char buff[1024];
f_getcwd(buff, sizeof(buff)-1); f_getcwd(buff, sizeof(buff) - 1);
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(buff)); simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(buff));
} }
if (settingsPath) { if (settingsPath && strlen(settingsPath) > 0) {
simuSettingsDirectory = removeTrailingPathDelimiter(fixPathDelimiters(settingsPath)); simuSettingsDirectory = removeTrailingPathDelimiter(fixPathDelimiters(settingsPath));
} }
TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSdDirectory: \"%s\"", simuSdDirectory.c_str()); TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSdDirectory: \"%s\"", simuSdDirectory.c_str());
-46
View File
@@ -57,51 +57,7 @@ static pixel_t _LCD_BUF2[DISPLAY_BUFFER_SIZE] __SDRAM;
pixel_t* simuLcdBuf = _LCD_BUF1; pixel_t* simuLcdBuf = _LCD_BUF1;
pixel_t* simuLcdBackBuf = _LCD_BUF2; 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) static void _copy_screen_area(uint16_t* dst, uint16_t* src, const lv_area_t& copy_area)
{ {
lv_coord_t x1 = copy_area.x1; 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; simuLcdRefresh = true;
#else #else
// // copy / rotate current area
// _copy_rotate_180(simuLcdBackBuf, buffer, copy_area);
_copy_area(simuLcdBackBuf, buffer, copy_area); _copy_area(simuLcdBackBuf, buffer, copy_area);
if (lv_disp_flush_is_last(disp_drv)) { if (lv_disp_flush_is_last(disp_drv)) {
+8 -5
View File
@@ -22,11 +22,12 @@
#include "hal/switch_driver.h" #include "hal/switch_driver.h"
#include "definitions.h" #include "definitions.h"
#include "myeeprom.h" #include "myeeprom.h"
#include <stdlib.h>
#include <assert.h>
#include "switches.h" #include "switches.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct hw_switch_def { struct hw_switch_def {
const char* name; const char* name;
SwitchHwType type; SwitchHwType type;
@@ -34,7 +35,7 @@ struct hw_switch_def {
#include "simu_switches.inc" #include "simu_switches.inc"
int8_t switchesStates[MAX_SWITCHES] = { -1 }; int8_t switchesStates[MAX_SWITCHES] = { 0 };
void simuSetSwitch(uint8_t swtch, int8_t state) void simuSetSwitch(uint8_t swtch, int8_t state)
{ {
@@ -42,7 +43,9 @@ void simuSetSwitch(uint8_t swtch, int8_t state)
switchesStates[swtch] = 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) static uint8_t get_switch_index(uint8_t cat, uint8_t idx)
{ {
+222
View File
@@ -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;
}
+51
View File
@@ -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 backlightEnable(uint8_t level) {}
void backlightFullOn() {} void backlightFullOn() {}
void backlightDisable() {} void backlightDisable() {}
uint8_t isBacklightEnabled() {return false;} bool isBacklightEnabled() { return false; }
#elif defined(PCBX9E) #elif defined(PCBX9E)
void backlightInit() void backlightInit()
{ {
@@ -65,7 +65,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR2 = 0; BACKLIGHT_TIMER->CCR2 = 0;
} }
uint8_t isBacklightEnabled() bool isBacklightEnabled()
{ {
return (BACKLIGHT_TIMER->CCR1 != 0 || BACKLIGHT_TIMER->CCR2 != 0); return (BACKLIGHT_TIMER->CCR1 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
} }
@@ -103,7 +103,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR2 = 0; BACKLIGHT_TIMER->CCR2 = 0;
} }
uint8_t isBacklightEnabled() bool isBacklightEnabled()
{ {
return (BACKLIGHT_TIMER->CCR4 != 0 || BACKLIGHT_TIMER->CCR2 != 0); return (BACKLIGHT_TIMER->CCR4 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
} }
@@ -144,7 +144,7 @@ void backlightDisable()
BACKLIGHT_COUNTER_REGISTER = 0; BACKLIGHT_COUNTER_REGISTER = 0;
} }
uint8_t isBacklightEnabled() bool isBacklightEnabled()
{ {
return BACKLIGHT_COUNTER_REGISTER != 0; return BACKLIGHT_COUNTER_REGISTER != 0;
} }
@@ -178,7 +178,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR1 = 0; BACKLIGHT_TIMER->CCR1 = 0;
} }
uint8_t isBacklightEnabled() bool isBacklightEnabled()
{ {
return BACKLIGHT_TIMER->CCR1 != 0; return BACKLIGHT_TIMER->CCR1 != 0;
} }
+1 -1
View File
@@ -202,7 +202,7 @@ void pwrResetHandler();
void backlightInit(); void backlightInit();
void backlightDisable(); void backlightDisable();
void backlightFullOn(); void backlightFullOn();
uint8_t isBacklightEnabled(); bool isBacklightEnabled();
#if defined(PCBX9E) || defined(PCBX9DP) #if defined(PCBX9E) || defined(PCBX9DP)
void backlightEnable(uint8_t level, uint8_t color); void backlightEnable(uint8_t level, uint8_t color);
-12
View File
@@ -128,18 +128,6 @@ uint16_t simu_get_analog(uint8_t idx)
return 0; return 0;
} }
void fsLedRGB(uint8_t idx, uint32_t color)
{
}
void fsLedOn(uint8_t idx)
{
}
void fsLedOff(uint8_t idx)
{
}
static char _stringResult[200]; static char _stringResult[200];
const char * nchar2string(const char * string, int size) const char * nchar2string(const char * string, int size)
+1 -1
View File
@@ -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_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err) #define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr) #define f_tell(fp) ((fp)->fptr)
#if defined(SIMU) #if defined(SIMU) && !defined(SIMU_DISKIO)
UINT f_size(FIL* fil); UINT f_size(FIL* fil);
#else #else
#define f_size(fp) ((fp)->obj.objsize) #define f_size(fp) ((fp)->obj.objsize)