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_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
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)
# if(WIN32)
# set(WIN_EXTRA_LIBS_PATH "C:/Programs" CACHE PATH
# "Base path to extra libs/headers on Windows (SDL & pthreads folders should be in here).")
# list(APPEND CMAKE_PREFIX_PATH "${WIN_EXTRA_LIBS_PATH}" "${WIN_EXTRA_LIBS_PATH}/SDL") # hints for FindSDL
# endif()
# libfox
find_package(Fox QUIET) # QUIET not working on WIN32?
if (FOX_FOUND)
message(STATUS "Foxlib found at ${FOX_LIBRARY}")
else()
message("Libfox not found, simu target will not be available")
endif()
if(NOT DISABLE_COMPANION)
include(QtDefs)
endif(NOT DISABLE_COMPANION)
if(Qt5Core_FOUND OR FOX_FOUND)
set(SDL2_BUILDING_LIBRARY YES) # this prevents FindSDL from appending SDLmain lib to the results, which we don't want
find_package("SDL2")
if(SDL2_FOUND)
# find_package("SDL2") does not set a variable holding the path to the location of the SDL2 shared library
find_file(SDL2_LIB_PATH
NAMES
libSDL2.so
SDL2.dll
SDL2.dylib
HINTS
"/usr/lib/x86_64-linux-gnu"
${SDL2_LIBRARY_PATH})
message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}")
else()
message(STATUS "SDL2 not found! Simulator audio, and joystick inputs, will not work.")
endif()
# this prevents FindSDL from appending SDLmain lib to the results, which we don't want
set(SDL2_BUILDING_LIBRARY YES)
find_package("SDL2")
if(SDL2_FOUND)
# find_package("SDL2") does not set a variable holding the path to the location of the SDL2 shared library
find_file(SDL2_LIB_PATH
NAMES
libSDL2.so
SDL2.dll
SDL2.dylib
HINTS
"/usr/lib/x86_64-linux-gnu"
${SDL2_LIBRARY_PATH})
message(STATUS "SDL2 Lib: ${SDL2_LIB_PATH} Libs: ${SDL2_LIBRARIES}; Headers: ${SDL2_INCLUDE_DIRS}")
else()
message(STATUS "SDL not found! Simulator audio, and joystick inputs, will not work.")
endif()
if(Qt5Core_FOUND AND NOT DISABLE_COMPANION)
+8 -8
View File
@@ -6,14 +6,14 @@ endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5Core)
find_package(Qt5Widgets)
find_package(Qt5Xml)
find_package(Qt5LinguistTools)
find_package(Qt5PrintSupport)
find_package(Qt5Multimedia)
find_package(Qt5Svg)
find_package(Qt5SerialPort)
find_package(Qt5Core QUIET)
find_package(Qt5Widgets QUIET)
find_package(Qt5Xml QUIET)
find_package(Qt5LinguistTools QUIET)
find_package(Qt5PrintSupport QUIET)
find_package(Qt5Multimedia QUIET)
find_package(Qt5Svg QUIET)
find_package(Qt5SerialPort QUIET)
if(Qt5Core_FOUND)
message(STATUS "Qt Version: ${Qt5Core_VERSION}")
-11
View File
@@ -28,9 +28,6 @@ option(SEMIHOSTING "Enable debugger semihosting" OFF)
option(JITTER_MEASURE "Enable ADC jitter measurement" OFF)
option(WATCHDOG "Enable hardware Watchdog" ON)
option(ASTERISK "Enable asterisk icon (test only firmware)" OFF)
if(SDL2_FOUND)
option(SIMU_AUDIO "Enable simulator audio." ON)
endif()
option(LUA "Enable LUA support" ON)
option(SIMU_DISKIO "Enable disk IO simulation in simulator. Simulator will use FatFs module and simulated IO layer that uses \"./sdcard.image\" file as image of SD card. This file must contain whole SD card from first to last sector" OFF)
option(SIMU_LUA_COMPILER "Pre-compile and save Lua scripts in simulator." ON)
@@ -245,10 +242,6 @@ if(WATCHDOG AND NOT NATIVE_BUILD)
add_definitions(-DUSE_WATCHDOG)
endif()
if(SIMU_AUDIO)
add_definitions(-DSIMU_AUDIO)
endif()
if(SIMU_DISKIO)
add_definitions(-DSIMU_DISKIO)
endif()
@@ -493,10 +486,6 @@ endif()
if(NATIVE_BUILD)
set(RADIOLIB_NATIVE_SRC ${SRC})
if(SIMU_DISKIO)
set(RADIOLIB_NATIVE_SRC ${RADIOLIB_NATIVE_SRC}
${FATFS_DIR}/ff.c ${FATFS_DIR}/ffunicode.c)
endif()
# Pack all radio sources into an object lib
add_library(radiolib_native OBJECT EXCLUDE_FROM_ALL
+24 -12
View File
@@ -370,6 +370,17 @@ AudioQueue::AudioQueue()
#define CODEC_ID_PCM_S16LE 1
static void _audio_lock()
{
mutex_lock(&audioMutex);
}
static void _audio_unlock()
{
mutex_unlock(&audioMutex);
}
#if !defined(__SSAT)
#define _sat_s16(x) ((int16_t)limit<int32_t>(INT16_MIN, (x), INT16_MAX))
#else
@@ -613,9 +624,9 @@ void AudioQueue::wakeup()
// mix the normal context (tones and wavs)
if (normalContext.isEmpty() && !fragmentsFifo.empty()) {
mutex_lock(&audioMutex);
_audio_lock();
normalContext.setFragment(fragmentsFifo.get());
mutex_unlock(&audioMutex);
_audio_unlock();
}
result = normalContext.mixBuffer(buffer, g_eeGeneral.beepVolume, g_eeGeneral.wavVolume, fade);
if (result > 0) {
@@ -696,10 +707,11 @@ bool AudioQueue::isPlaying(uint8_t id)
void AudioQueue::playTone(uint16_t freq, uint16_t len, uint16_t pause, uint8_t flags, int8_t freqIncr, int8_t fragmentVolume)
{
#if defined(SIMU) && !defined(SIMU_AUDIO)
#warning "NO audio"
return;
#endif
mutex_lock(&audioMutex);
_audio_lock();
freq = limit<uint16_t>(BEEP_MIN_FREQ, freq, BEEP_MAX_FREQ);
@@ -722,7 +734,7 @@ void AudioQueue::playTone(uint16_t freq, uint16_t len, uint16_t pause, uint8_t f
}
}
mutex_unlock(&audioMutex);
_audio_unlock();
}
void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8_t fragmentVolume)
@@ -745,7 +757,7 @@ void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8
return;
}
mutex_lock(&audioMutex);
_audio_lock();
if (flags & PLAY_BACKGROUND) {
backgroundContext.clear();
@@ -755,7 +767,7 @@ void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id, int8
fragmentsFifo.push(AudioFragment(filename, flags & 0x0f, fragmentVolume, id));
}
mutex_unlock(&audioMutex);
_audio_unlock();
}
void AudioQueue::stopPlay(uint8_t id)
@@ -768,12 +780,12 @@ void AudioQueue::stopPlay(uint8_t id)
return;
#endif
mutex_lock(&audioMutex);
_audio_lock();
fragmentsFifo.removePromptById(id);
backgroundContext.stop(id);
mutex_unlock(&audioMutex);
_audio_unlock();
}
void AudioQueue::stopSD()
@@ -786,19 +798,19 @@ void AudioQueue::stopSD()
void AudioQueue::stopAll()
{
flush();
mutex_lock(&audioMutex);
_audio_lock();
priorityContext.clear();
normalContext.clear();
mutex_unlock(&audioMutex);
_audio_unlock();
}
void AudioQueue::flush()
{
mutex_lock(&audioMutex);
_audio_lock();
fragmentsFifo.clear();
varioContext.clear();
backgroundContext.clear();
mutex_unlock(&audioMutex);
_audio_unlock();
}
void audioPlay(unsigned int index, uint8_t id)
-1
View File
@@ -469,7 +469,6 @@ enum {
void codecsInit();
void audioEvent(unsigned int index);
void audioPlay(unsigned int index, uint8_t id=0);
void audioStart();
#if defined(AUDIO) && defined(BUZZER)
#define AUDIO_BUZZER(a, b) do { a; b; } while(0)
+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_2ND_COLUMN + 5, y, getFSLogicalState(i) ? STR_CHAR_DOWN : STR_CHAR_UP);
#if defined(FUNCTION_SWITCHES_RGB_LEDS)
lcdDrawText(FS_3RD_COLUMN, y, STR_FS_COLOR_LIST[getRGBColorIndex(rgbGetLedColor(i))], 0);
lcdDrawText(FS_3RD_COLUMN, y, STR_FS_COLOR_LIST[getRGBColorIndex(fsGetLedRGB(i))], 0);
#else
lcdDrawText(FS_3RD_COLUMN, y, STR_OFFON[fsLedState(i)]);
#endif
+1 -5
View File
@@ -102,11 +102,7 @@ void waitSplash()
{
// Handle color splash screen
if (splashStartTime) {
#if defined(SIMU)
// Simulator - inputsMoved() returns true immediately without this!
sleep_ms(30);
#endif // defined(SIMU)
inactivityCheckInputs();
splashStartTime += SPLASH_TIMEOUT;
while (splashStartTime >= get_tmr10ms()) {
LvglWrapper::instance()->run();
+1 -1
View File
@@ -30,7 +30,7 @@ set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
${CMAKE_CURRENT_SOURCE_DIR}/hal/key_driver.cpp
)
if (NOT NATIVE_BUILD)
if ((NOT NATIVE_BUILD) OR SIMU_DISKIO)
set(SRC ${SRC}
hal/storage.cpp
hal/fatfs_diskio.cpp
+4 -1
View File
@@ -24,7 +24,10 @@
#include "debug.h"
#if defined(STORAGE_USE_SDIO)
#if defined(SIMU_DISKIO)
extern const diskio_driver_t simu_diskio_driver;
#define _STORAGE_DRIVER simu_diskio_driver
#elif defined(STORAGE_USE_SDIO)
#include "diskio_sdio.h"
#define _STORAGE_DRIVER sdio_diskio_driver
#elif defined(STORAGE_USE_SDCARD_SPI)
+6 -1
View File
@@ -52,6 +52,11 @@ void inactivityTimerReset(ActivitySource src)
#define INAC_STICKS_SHIFT 7
#define INAC_SWITCHES_SHIFT 8
static inline int8_t inactivity_diff(uint8_t sum)
{
return abs((int8_t)(sum - inactivity.sum));
}
bool inactivityCheckInputs()
{
uint8_t sum = 0;
@@ -78,7 +83,7 @@ bool inactivityCheckInputs()
sum += get_spacemouse_value(i) >> INAC_STICKS_SHIFT;
#endif
if (abs((int8_t)(sum - inactivity.sum)) > 1) {
if (inactivity_diff(sum) > 1) {
inactivity.sum = sum;
return true;
} else {
+1 -1
View File
@@ -109,7 +109,7 @@ FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime)
return res;
}
#if !defined(BOOT) && !defined(SIMU)
#if !defined(BOOT) && (!defined(SIMU) || defined(SIMU_DISKIO))
// Replace FatFS implementation of f_puts and f_printf
+2
View File
@@ -4,6 +4,8 @@
#include <algorithm>
using namespace std::chrono_literals;
static timer_queue* _instance = nullptr;
static std::mutex _instance_mut;
+1 -1
View File
@@ -565,7 +565,7 @@ void sdDone()
uint32_t sdMounted()
{
#if defined(SIMU)
#if defined(SIMU) && !defined(SIMU_DISKIO)
return true;
#else
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
-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
#include <cinttypes>
#include <stdint.h>
#include <stdlib.h>
#include "edgetx_types.h"
#include "edgetx_constants.h"
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)
message(STATUS "libsimulator/simu targets disabled")
return()
endif()
target_compile_options(radiolib_native PUBLIC -DSIMU)
if(SDL2_FOUND)
option(SIMU_AUDIO "Enable simulator audio." ON)
if(SIMU_AUDIO)
target_compile_options(radiolib_native PUBLIC -DSIMU_AUDIO)
endif()
endif()
set(SIMU_DRIVERS
simpgmspace.cpp
simufatfs.cpp
simudisk.cpp
simulcd.cpp
simuaudio.cpp
switch_driver.cpp
adc_driver.cpp
module_drivers.cpp
@@ -156,60 +166,44 @@ if(WIN32)
endif()
endif(WIN32)
if(MSVC)
set(CMAKE_CXX_FLAGS "/EHsc")
if(NOT CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /LD /MP")
endif()
else()
if(MINGW)
# struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
if(ASAN)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
endif()
if(MINGW)
# struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
if(ASAN)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
add_executable(simu
EXCLUDE_FROM_ALL
${SIMU_SRC}
display.cpp
widgets.cpp
knobs.cpp
sdl_simu.cpp
)
if(FOX_FOUND)
add_executable(simu WIN32
EXCLUDE_FROM_ALL
${SIMU_SRC}
${RADIO_SRC_DIR}/simu.cpp)
include(FetchImgui)
target_compile_options(simu PUBLIC -DSIMU)
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} )
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL2_LIBRARIES})
target_compile_options(simu PRIVATE ${SIMU_SRC_OPTIONS})
endif()
add_png_target(image_assets "assets/images/*.png")
add_dependencies(simu image_assets)
if(APPLE)
# OS X compiler no longer automatically includes /Library/Frameworks in search path
set(CMAKE_SHARED_LINKER_FLAGS -F/Library/Frameworks)
set(STB_DIR ${RADIO_SRC_DIR}/thirdparty/stb)
target_include_directories(simu PUBLIC ${STB_DIR})
target_link_libraries(simu PUBLIC imgui)
set(SIMULATOR_BUNDLES)
foreach(library ${OPENTX_LIBRARIES})
set(SIMULATOR_BUNDLE "${library}-bundle")
add_custom_target(${SIMULATOR_BUNDLE}
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore lib${library}.dylib
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork lib${library}.dylib
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtXml.framework/Versions/4/QtXml @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml lib${library}.dylib
COMMAND install_name_tool -change /opt/local/Library/Frameworks/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui lib${library}.dylib
COMMAND install_name_tool -change @rpath/SDL.framework/Versions/A/SDL @executable_path/../Frameworks/SDL.framework/Versions/A/SDL lib${library}.dylib
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMAND pwd
COMMAND cp lib${library}.dylib companion.app/Contents/Resources/
DEPENDS ${library}
)
list(APPEND SIMULATOR_BUNDLES ${SIMULATOR_BUNDLE})
endforeach()
add_custom_target(opentx-simulators-bundle DEPENDS ${SIMULATOR_BUNDLES})
endif(APPLE)
target_link_libraries(simu PUBLIC
pthread
${SDL2_LIBRARIES}
)
PrintTargetReport("simu/libsimulator")
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.
*/
#include "board.h"
bool boardBacklightOn = false;
bool isBacklightEnabled() { return boardBacklightOn; }
void backlightFullOn() { boardBacklightOn = true; }
void backlightInit() {}
void backlightEnable(unsigned char) {}
void backlightEnable(unsigned char, unsigned char) {}
void backlightDisable() {}
#if !defined(COLORLCD)
void backlightFullOn() { boardBacklightOn = true; }
void backlightEnable(unsigned char)
{
boardBacklightOn = true;
}
void backlightEnable(unsigned char, unsigned char)
{
boardBacklightOn = true;
}
void backlightDisable()
{
boardBacklightOn = false;
}
#else
void backlightFullOn() { backlightEnable(BACKLIGHT_LEVEL_MAX); }
void backlightEnable(uint8_t) {}
#endif
+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.
*/
void gyroInit() {}
void gyroRead() {}
void gyroRead(unsigned char*) {}
#include "gyro.h"
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 "definitions.h"
#include "board.h"
bool usbChargerLed() { return true; }
void ledRed() {}
void ledGreen() {}
void ledBlue() {}
void ledOff() {}
bool fsLedState(uint8_t) { return false;}
void rgbSetLedColor(unsigned char, unsigned char, unsigned char, unsigned char) {}
void rgbSetLedColor(uint8_t, uint8_t, uint8_t, uint8_t) {}
void rgbLedColorApply() {}
uint8_t getRGBColorIndex(uint32_t color)
@@ -41,24 +43,39 @@ uint8_t getRGBColorIndex(uint32_t color)
return 0; // Custom value set with Companion
}
// RGB
#define WS2812_BYTES_PER_LED 3
#if NUM_FUNCTIONS_SWITCHES > 0
static uint32_t _fs_switch_colors[NUM_FUNCTIONS_SWITCHES] = {0};
static uint32_t _fs_switch_color_mask = 0;
static uint32_t _fs_switch_mask = 0;
// Maximum number of supported LEDs
#if !defined(WS2812_MAX_LEDS)
# define WS2812_MAX_LEDS 48
#endif
// Pixel values
static uint8_t _led_colors[WS2812_BYTES_PER_LED * WS2812_MAX_LEDS];
uint32_t rgbGetLedColor(uint8_t led)
void fsLedRGB(uint8_t index, uint32_t col)
{
uint8_t* pixel = &_led_colors[led * WS2812_BYTES_PER_LED];
return (pixel[1] << 16) + (pixel[0] << 8) + pixel[2];
_fs_switch_color_mask |= (1 << index);
_fs_switch_colors[index] = col;
}
uint32_t fsGetLedRGB(uint8_t index)
{
return rgbGetLedColor(index);
return _fs_switch_colors[index];
}
bool fsLedIsColorSet(uint8_t index)
{
return _fs_switch_color_mask & (1 << index);
}
bool fsLedState(uint8_t index)
{
return _fs_switch_mask & (1 << index);
}
void fsLedOn(uint8_t index)
{
_fs_switch_mask |= (1 << index);
}
void fsLedOff(uint8_t index)
{
_fs_switch_mask &= ~(1 << index);
}
#endif
+3 -13
View File
@@ -22,6 +22,7 @@
#include "opentxsimulator.h"
#include "edgetx.h"
#include "simulcd.h"
#include "simuaudio.h"
#include "switches.h"
#include "serial.h"
#include "myeeprom.h"
@@ -89,18 +90,6 @@ void firmwareTraceCb(const char * text)
}
}
void fsLedRGB(uint8_t idx, uint32_t color)
{
}
void fsLedOn(uint8_t idx)
{
}
void fsLedOff(uint8_t idx)
{
}
// Serial port handling needs to know about OpenTxSimulator, so we we
// need to update what's in simpgmspace.cpp when we have a simulator
// to point at.
@@ -283,7 +272,7 @@ void OpenTxSimulator::start(const char * filename, bool tests)
QMutexLocker lckr(&m_mtxSimuMain);
QMutexLocker slckr(&m_mtxSettings);
startAudio(volumeGain);
simuAudioInit();
simuStart(tests, simuSdDirectory.toLatin1().constData(),
simuSettingsDirectory.toLatin1().constData());
@@ -301,6 +290,7 @@ void OpenTxSimulator::stop()
QMutexLocker lckr(&m_mtxSimuMain);
simuStop();
simuAudioDeInit();
emit stopped();
}
+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
adcInit(&simu_adc_driver);
// Switches
switchInit();
}
bool keysStates[MAX_KEYS] = { false };
@@ -205,12 +207,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
}
#endif
#if defined(SIMU_EXCEPTIONS)
signal(SIGFPE, sig);
signal(SIGSEGV, sig);
try {
#endif
// Init LCD callbacks
lcdInit();
@@ -228,12 +224,6 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath)
#endif
simu_running = true;
#if defined(SIMU_EXCEPTIONS)
}
catch (...) {
}
#endif
}
extern task_handle_t mixerTaskId;
@@ -250,130 +240,14 @@ void simuStop()
simu_shutdown = true;
task_shutdown_all();
#if defined(SIMU_AUDIO)
stopAudio();
#endif
simu_running = false;
}
struct SimulatorAudio {
int volumeGain;
int currentVolume;
int16_t leftoverData[AUDIO_BUFFER_SIZE];
int leftoverLen;
} simuAudio;
bool simuIsRunning()
{
return simu_running;
}
void audioConsumeCurrentBuffer()
{
}
void audioSetVolume(uint8_t volume)
{
simuAudio.currentVolume = 127 * volume * simuAudio.volumeGain / VOLUME_LEVEL_MAX / 10;
// TRACE_SIMPGMSPACE("setVolume(): in: %u, out: %u", volume, simuAudio.currentVolume);
}
#if defined(SIMU_AUDIO)
void copyBuffer(void* dest, const int16_t* buff, unsigned samples)
{
int16_t* i16_dst = (int16_t*)dest;
for (unsigned i = 0; i < samples; i++) {
int32_t sample = (((int32_t)buff[i] * (int32_t)simuAudio.currentVolume) / 127);
if (sample > INT16_MAX) sample = INT16_MAX;
else if (sample < INT16_MIN) sample = INT16_MIN;
*(i16_dst++) = (int16_t)sample;
}
}
void fillAudioBuffer(void *udata, Uint8 *stream, int len)
{
SDL_memset(stream, 0, len);
if (simuAudio.leftoverLen) {
int len1 = min(len/2, simuAudio.leftoverLen);
copyBuffer(stream, simuAudio.leftoverData, len1);
len -= len1*2;
stream += len1*2;
simuAudio.leftoverLen -= len1;
// putchar('l');
if (simuAudio.leftoverLen) return; // buffer fully filled
}
if (audioQueue.buffersFifo.filledAtleast(len / (AUDIO_BUFFER_SIZE * 2) + 1)) {
while (true) {
const AudioBuffer* nextBuffer =
audioQueue.buffersFifo.getNextFilledBuffer();
if (nextBuffer) {
if (len >= nextBuffer->size * 2) {
copyBuffer(stream, nextBuffer->data, nextBuffer->size);
stream += nextBuffer->size * 2;
len -= nextBuffer->size * 2;
// putchar('+');
audioQueue.buffersFifo.freeNextFilledBuffer();
} else {
// partial
copyBuffer(stream, nextBuffer->data, len / 2);
simuAudio.leftoverLen = (nextBuffer->size - len / 2);
memcpy(simuAudio.leftoverData, &nextBuffer->data[len / 2],
simuAudio.leftoverLen * 2);
len = 0;
// putchar('p');
audioQueue.buffersFifo.freeNextFilledBuffer();
break;
}
} else {
break;
}
}
}
// fill the rest of buffer with silence
if (len > 0) {
SDL_memset(stream, 0x8000, len); // make sure this is silence.
}
}
int startAudio(int volumeGain)
{
simuAudio = {
.volumeGain = volumeGain,
.leftoverLen = 0,
};
TRACE("startAudioThread(%d)", volumeGain);
audioSetVolume(VOLUME_LEVEL_DEF);
/* Set the audio format */
SDL_AudioSpec desired = {
.freq = AUDIO_SAMPLE_RATE,
.format = AUDIO_S16SYS,
.channels = 1,
.samples = AUDIO_BUFFER_SIZE * 2,
.callback = fillAudioBuffer,
.userdata = nullptr,
};
SDL_AudioSpec obtained;
if ( SDL_OpenAudio(&desired, &obtained) < 0 ) {
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
return -1;
}
SDL_PauseAudio(0);
return 0;
}
void stopAudio()
{
SDL_CloseAudio();
}
#endif // #if defined(SIMU_AUDIO)
#if !defined(COLORLCD)
void lcdSetRefVolt(uint8_t val)
{
@@ -617,6 +491,12 @@ const etx_serial_port_t* auxSerialGetPort(int port_nr)
struct TouchState simTouchState = {};
bool simTouchOccured = false;
bool touchPanelInit()
{
simTouchState.x = simTouchState.y = 0;
return true;
}
bool touchPanelEventOccured()
{
if(simTouchOccured)
@@ -627,6 +507,20 @@ bool touchPanelEventOccured()
return false;
}
void touchPanelDown(short x, short y)
{
simTouchState.x = x;
simTouchState.y = y;
simTouchState.event = TE_DOWN;
simTouchOccured = true;
}
void touchPanelUp()
{
simTouchState.event = TE_UP;
simTouchOccured = true;
}
struct TouchState touchPanelRead()
{
struct TouchState st = simTouchState;
+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)
#include "edgetx.h"
#include "ff.h"
#include "diskio.h"
#include <time.h>
#include "hal/fatfs_diskio.h"
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
FILE * diskImage = 0;
extern const diskio_driver_t simu_diskio_driver;
bool _g_FATFS_init = false;
static FILE* disk_image = nullptr;
RTOS_MUTEX_HANDLE ioMutex;
// create FS with: mkdosfs -n SDCARD -S 512 -C sdcard.img 524288
static const char* image_path = "sdcard.img";
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj) /* Create a sync object */
static int _seek(DWORD sector)
{
pthread_mutex_init(&ioMutex, 0);
return 1;
}
int ff_req_grant (FF_SYNC_t sobj) /* Lock sync object */
{
pthread_mutex_lock(&ioMutex);
return 1;
}
void ff_rel_grant (FF_SYNC_t sobj) /* Unlock sync object */
{
pthread_mutex_unlock(&ioMutex);
}
int ff_del_syncobj (FF_SYNC_t sobj) /* Delete a sync object */
{
pthread_mutex_destroy(&ioMutex);
return 1;
}
DWORD get_fattime (void)
{
time_t tim = time(0);
const struct tm * t = gmtime(&tim);
/* Pack date and time into a DWORD variable */
return ((DWORD)(t->tm_year - 80) << 25)
| ((uint32_t)(t->tm_mon+1) << 21)
| ((uint32_t)t->tm_mday << 16)
| ((uint32_t)t->tm_hour << 11)
| ((uint32_t)t->tm_min << 5)
| ((uint32_t)t->tm_sec >> 1);
}
unsigned int noDiskStatus = 0;
void traceDiskStatus()
{
if (noDiskStatus > 0) {
TRACE_SIMPGMSPACE("disk_status() called %d times", noDiskStatus);
noDiskStatus = 0;
if (!disk_image) {
fprintf(stderr, "disk image is NULL\n");
return -1;
}
if (fseek(disk_image, sector * 512, SEEK_SET) != 0) {
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
DSTATUS disk_initialize (BYTE pdrv)
static DSTATUS simu_disk_initialize(BYTE lun)
{
traceDiskStatus();
TRACE_SIMPGMSPACE("disk_initialize(%u)", pdrv);
diskImage = fopen("sdcard.image", "rb+");
return diskImage ? (DSTATUS)0 : (DSTATUS)STA_NODISK;
}
if (!image_path) return STA_NODISK;
disk_image = fopen(image_path, "rb+");
if (!disk_image) {
fprintf(stderr, "could not open disk image: %s\n", strerror(errno));
return STA_NODISK;
}
DSTATUS disk_status (BYTE pdrv)
{
++noDiskStatus;
// TRACE_SIMPGMSPACE("disk_status(%u)", pdrv);
return (DSTATUS)0;
}
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
{
if (diskImage == 0) return RES_NOTRDY;
traceDiskStatus();
TRACE_SIMPGMSPACE("disk_read(%u, %p, %u, %u)", pdrv, buff, sector, count);
fseek(diskImage, sector*512, SEEK_SET);
fread(buff, count, 512, diskImage);
return RES_OK;
}
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
static DSTATUS simu_disk_status(BYTE lun) { return RES_OK; }
static DRESULT simu_disk_read(BYTE lun, BYTE* buff, DWORD sector, UINT count)
{
if (diskImage == 0) return RES_NOTRDY;
traceDiskStatus();
TRACE_SIMPGMSPACE("disk_write(%u, %p, %u, %u)", pdrv, buff, sector, count);
fseek(diskImage, sector*512, SEEK_SET);
fwrite(buff, count, 512, diskImage);
if (_seek(sector) != 0) return RES_ERROR;
fprintf(stderr, "# R %d/%d\n", sector, count);
fread(buff, 512, count, disk_image);
return RES_OK;
}
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff)
static DRESULT simu_disk_write(BYTE lun, const BYTE* buff, DWORD sector, UINT count)
{
if (diskImage == 0) return RES_NOTRDY;
traceDiskStatus();
TRACE_SIMPGMSPACE("disk_ioctl(%u, %u, %p)", pdrv, cmd, buff);
if (pdrv) return RES_PARERR;
if (_seek(sector) != 0) return RES_ERROR;
fprintf(stderr, "# W %d/%d\n", sector, count);
fwrite(buff, 512, count, disk_image);
return RES_OK;
}
DRESULT res;
BYTE *ptr = (BYTE *)buff;
if (cmd == CTRL_POWER) {
switch (*ptr) {
case 0: /* Sub control code == 0 (POWER_OFF) */
res = RES_OK;
break;
case 1: /* Sub control code == 1 (POWER_ON) */
res = RES_OK;
break;
case 2: /* Sub control code == 2 (POWER_GET) */
*(ptr+1) = (BYTE)1; /* fake powered */
res = RES_OK;
break;
default :
res = RES_PARERR;
}
return res;
}
static DRESULT simu_disk_ioctl(BYTE lun, BYTE cmd, void* buff)
{
if (!disk_image) return RES_NOTRDY;
DRESULT res = RES_OK;
switch(cmd) {
/* Generic command (Used by FatFs) */
case CTRL_SYNC : /* Complete pending write process (needed at _FS_READONLY == 0) */
break;
case GET_SECTOR_COUNT: /* Get media size (needed at _USE_MKFS == 1) */
{
struct stat buf;
if (stat("sdcard.image", &buf) == 0) {
DWORD noSectors = buf.st_size / 512;
*(DWORD*)buff = noSectors;
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_COUNT) = %u", noSectors);
return RES_OK;
}
return RES_ERROR;
case GET_SECTOR_COUNT:
if (fseek(disk_image, 0, SEEK_END) == 0) {
long sectors = ftell(disk_image) / 512L;
fprintf(stderr, "# S %ld\n", sectors);
*((DWORD*)buff) = (DWORD)(sectors);
} else {
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
res = RES_ERROR;
}
break;
case GET_SECTOR_SIZE: /* Get sector size (needed at _MAX_SS != _MIN_SS) */
TRACE_SIMPGMSPACE("disk_ioctl(GET_SECTOR_SIZE) = 512");
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : /* Get erase block size (needed at _USE_MKFS == 1) */
case GET_BLOCK_SIZE:
*(WORD*)buff = 512 * 4;
res = RES_OK;
break;
case CTRL_TRIM : /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
break;
/* Generic command (Not used by FatFs) */
case CTRL_LOCK : /* Lock/Unlock media removal */
case CTRL_EJECT: /* Eject media */
case CTRL_FORMAT: /* Create physical format on the media */
return RES_PARERR;
/* MMC/SDC specific ioctl command */
// case MMC_GET_TYPE 10 /* Get card type */
// case MMC_GET_CSD 11 /* Get CSD */
// case MMC_GET_CID 12 /* Get CID */
// case MMC_GET_OCR 13 /* Get OCR */
// case MMC_GET_SDSTAT 14 /* Get SD status */
/* ATA/CF specific ioctl command */
// case ATA_GET_REV 20 /* Get F/W revision */
// case ATA_GET_MODEL 21 /* Get model name */
// case ATA_GET_SN 22 /* Get serial number */
default:
return RES_PARERR;
break;
}
return RES_OK;
return res;
}
void sdInit(void)
{
// ioMutex = CoCreateMutex();
// if (ioMutex >= CFG_MAX_MUTEX ) {
// // sd error
// return;
// }
if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) {
// call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called
sdGetFreeSectors();
#if defined(LOG_TELEMETRY)
f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE);
if (f_size(&g_telemetryFile) > 0) {
f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append
}
#endif
#if defined(LOG_BLUETOOTH)
f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE);
if (f_size(&g_bluetoothFile) > 0) {
f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append
}
#endif
}
else {
TRACE_SIMPGMSPACE("f_mount() failed");
}
}
void sdDone()
{
if (sdMounted()) {
audioQueue.stopSD();
#if defined(LOG_TELEMETRY)
f_close(&g_telemetryFile);
#endif
#if defined(LOG_BLUETOOTH)
f_close(&g_bluetoothFile);
#endif
f_mount(NULL, "", 0); // unmount SD
}
}
void sdMount()
{
TRACE("sdMount");
diskCache.clear();
if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) {
// call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called
_g_FATFS_init = true;
sdGetFreeSectors();
#if defined(LOG_TELEMETRY)
f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE);
if (f_size(&g_telemetryFile) > 0) {
f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append
}
#endif
#if defined(LOG_BLUETOOTH)
f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE);
if (f_size(&g_bluetoothFile) > 0) {
f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append
}
#endif
}
else {
TRACE("f_mount() failed");
}
}
uint32_t sdMounted()
{
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
}
uint32_t sdIsHC()
{
return sdGetSize() > 2000000;
}
uint32_t sdGetSpeed()
{
return 330000;
}
const diskio_driver_t simu_diskio_driver = {
.initialize = simu_disk_initialize,
.status = simu_disk_status,
.read = simu_disk_read,
.write = simu_disk_write,
.ioctl = simu_disk_ioctl,
};
#endif // #if defined(SIMU_DISKIO)
+4 -5
View File
@@ -87,15 +87,14 @@ std::string fixPathDelimiters(const char * path)
void simuFatfsSetPaths(const char * sdPath, const char * settingsPath)
{
if (sdPath) {
if (sdPath && strlen(sdPath) > 0) {
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(sdPath));
}
else {
} else if (simuSdDirectory.empty()) {
char buff[1024];
f_getcwd(buff, sizeof(buff)-1);
f_getcwd(buff, sizeof(buff) - 1);
simuSdDirectory = removeTrailingPathDelimiter(fixPathDelimiters(buff));
}
if (settingsPath) {
if (settingsPath && strlen(settingsPath) > 0) {
simuSettingsDirectory = removeTrailingPathDelimiter(fixPathDelimiters(settingsPath));
}
TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSdDirectory: \"%s\"", simuSdDirectory.c_str());
-46
View File
@@ -57,51 +57,7 @@ static pixel_t _LCD_BUF2[DISPLAY_BUFFER_SIZE] __SDRAM;
pixel_t* simuLcdBuf = _LCD_BUF1;
pixel_t* simuLcdBackBuf = _LCD_BUF2;
#if 0
// Copy 2 pixels at once to speed up a little
static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_area)
{
coord_t x1 = LCD_W - copy_area.w - copy_area.x;
coord_t y1 = LCD_H - copy_area.h - copy_area.y;
auto total = copy_area.w * copy_area.h;
uint16_t* px_src = src + total - 2;
auto px_dst = dst + y1 * LCD_W + x1;
for (auto line = 0; line < copy_area.h; line++) {
auto line_end = px_dst + (copy_area.w & ~1);
while (px_dst != line_end) {
uint32_t* px2_src = (uint32_t*)px_src;
uint32_t* px2_dst = (uint32_t*)px_dst;
uint32_t px = ((*px2_src & 0xFFFF0000) >> 16) | ((*px2_src & 0xFFFF) << 16);
*px2_dst = px;
px_src -= 2;
px_dst += 2;
}
if (copy_area.w & 1) {
*(px_dst++) = *(px_src+1);
px_src--;
}
px_dst += LCD_W - copy_area.w;
}
}
static void _rotate_area_180(lv_area_t& area)
{
lv_coord_t tmp_coord;
tmp_coord = area.y2;
area.y2 = LCD_H - area.y1 - 1;
area.y1 = LCD_H - tmp_coord - 1;
tmp_coord = area.x2;
area.x2 = LCD_W - area.x1 - 1;
area.x1 = LCD_W - tmp_coord - 1;
}
#endif
static void _copy_screen_area(uint16_t* dst, uint16_t* src, const lv_area_t& copy_area)
{
lv_coord_t x1 = copy_area.x1;
@@ -150,8 +106,6 @@ static void simuRefreshLcd(lv_disp_drv_t * disp_drv, uint16_t *buffer, const rec
simuLcdRefresh = true;
#else
// // copy / rotate current area
// _copy_rotate_180(simuLcdBackBuf, buffer, copy_area);
_copy_area(simuLcdBackBuf, buffer, copy_area);
if (lv_disp_flush_is_last(disp_drv)) {
+8 -5
View File
@@ -22,11 +22,12 @@
#include "hal/switch_driver.h"
#include "definitions.h"
#include "myeeprom.h"
#include <stdlib.h>
#include <assert.h>
#include "switches.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct hw_switch_def {
const char* name;
SwitchHwType type;
@@ -34,7 +35,7 @@ struct hw_switch_def {
#include "simu_switches.inc"
int8_t switchesStates[MAX_SWITCHES] = { -1 };
int8_t switchesStates[MAX_SWITCHES] = { 0 };
void simuSetSwitch(uint8_t swtch, int8_t state)
{
@@ -42,7 +43,9 @@ void simuSetSwitch(uint8_t swtch, int8_t state)
switchesStates[swtch] = state;
}
void boardInitSwitches() {}
void boardInitSwitches() {
memset(switchesStates, -1, sizeof(switchesStates));
}
static uint8_t get_switch_index(uint8_t cat, uint8_t idx)
{
+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 backlightFullOn() {}
void backlightDisable() {}
uint8_t isBacklightEnabled() {return false;}
bool isBacklightEnabled() { return false; }
#elif defined(PCBX9E)
void backlightInit()
{
@@ -65,7 +65,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR2 = 0;
}
uint8_t isBacklightEnabled()
bool isBacklightEnabled()
{
return (BACKLIGHT_TIMER->CCR1 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
}
@@ -103,7 +103,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR2 = 0;
}
uint8_t isBacklightEnabled()
bool isBacklightEnabled()
{
return (BACKLIGHT_TIMER->CCR4 != 0 || BACKLIGHT_TIMER->CCR2 != 0);
}
@@ -144,7 +144,7 @@ void backlightDisable()
BACKLIGHT_COUNTER_REGISTER = 0;
}
uint8_t isBacklightEnabled()
bool isBacklightEnabled()
{
return BACKLIGHT_COUNTER_REGISTER != 0;
}
@@ -178,7 +178,7 @@ void backlightDisable()
BACKLIGHT_TIMER->CCR1 = 0;
}
uint8_t isBacklightEnabled()
bool isBacklightEnabled()
{
return BACKLIGHT_TIMER->CCR1 != 0;
}
+1 -1
View File
@@ -202,7 +202,7 @@ void pwrResetHandler();
void backlightInit();
void backlightDisable();
void backlightFullOn();
uint8_t isBacklightEnabled();
bool isBacklightEnabled();
#if defined(PCBX9E) || defined(PCBX9DP)
void backlightEnable(uint8_t level, uint8_t color);
-12
View File
@@ -128,18 +128,6 @@ uint16_t simu_get_analog(uint8_t idx)
return 0;
}
void fsLedRGB(uint8_t idx, uint32_t color)
{
}
void fsLedOn(uint8_t idx)
{
}
void fsLedOff(uint8_t idx)
{
}
static char _stringResult[200];
const char * nchar2string(const char * string, int size)
+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_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
#if defined(SIMU)
#if defined(SIMU) && !defined(SIMU_DISKIO)
UINT f_size(FIL* fil);
#else
#define f_size(fp) ((fp)->obj.objsize)