Backend abstraction + added android support + partial high DPI scaling

This commit is contained in:
AlexandreRouma 2022-02-13 17:25:23 +01:00
parent 9969ce018b
commit e5f3d1672c
36 changed files with 693 additions and 185 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ CMakeSettings.json
poggers_decoder poggers_decoder
m17_decoder/libcorrect m17_decoder/libcorrect
SDR++.app SDR++.app
android/deps
android/app/assets

View File

@ -7,8 +7,19 @@ else()
set(CMAKE_INSTALL_PREFIX "/usr") set(CMAKE_INSTALL_PREFIX "/usr")
endif() endif()
# Configure toolchain for android
if (ANDROID)
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate"
)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++17")
endif (ANDROID)
# Backends # Backends
option(OPT_BACKEND_GLFW "Use the GLFW backend" ON) option(OPT_BACKEND_GLFW "Use the GLFW backend" ON)
option(OPT_BACKEND_ANDROID "Use the Android backend" OFF)
# Compatibility Options # Compatibility Options
option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on systems that don't have it yet" OFF) option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on systems that don't have it yet" OFF)
@ -31,6 +42,7 @@ option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencie
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON) option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
# Sinks # Sinks
option(OPT_BUILD_ANDROID_AUDIO_SINK "Build Android Audio Sink Module (Dependencies: AAudio, only for android)" OFF)
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON) option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON)
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF) option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON) option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON)
@ -121,6 +133,10 @@ endif (OPT_BUILD_PLUTOSDR_SOURCE)
# Sink modules # Sink modules
if (OPT_BUILD_ANDROID_AUDIO_SINK)
add_subdirectory("sink_modules/android_audio_sink")
endif (OPT_BUILD_ANDROID_AUDIO_SINK)
if (OPT_BUILD_AUDIO_SINK) if (OPT_BUILD_AUDIO_SINK)
add_subdirectory("sink_modules/audio_sink") add_subdirectory("sink_modules/audio_sink")
endif (OPT_BUILD_AUDIO_SINK) endif (OPT_BUILD_AUDIO_SINK)

View File

@ -21,6 +21,10 @@ endif ()
if (OPT_BACKEND_GLFW) if (OPT_BACKEND_GLFW)
file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c") file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c")
endif (OPT_BACKEND_GLFW) endif (OPT_BACKEND_GLFW)
if (OPT_BACKEND_ANDROID)
file(GLOB_RECURSE BACKEND_SRC "backends/android/*.cpp" "backends/android/*.c")
set(BACKEND_SRC ${BACKEND_SRC} ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
endif (OPT_BACKEND_ANDROID)
# Add code to dyn lib # Add code to dyn lib
add_library(sdrpp_core SHARED ${SRC} ${BACKEND_SRC}) add_library(sdrpp_core SHARED ${SRC} ${BACKEND_SRC})
@ -59,6 +63,10 @@ if (OPT_BACKEND_GLFW)
target_link_libraries(sdrpp_core PUBLIC ${GLFW3_LIBRARIES}) target_link_libraries(sdrpp_core PUBLIC ${GLFW3_LIBRARIES})
endif() endif()
endif (OPT_BACKEND_GLFW) endif (OPT_BACKEND_GLFW)
if (OPT_BACKEND_ANDROID)
target_include_directories(sdrpp_core PUBLIC "backends/android")
target_include_directories(sdrpp_core PUBLIC "backends/android/imgui")
endif (OPT_BACKEND_ANDROID)
# Link to libcorrect # Link to libcorrect
if (USE_INTERNAL_LIBCORRECT) if (USE_INTERNAL_LIBCORRECT)
@ -98,7 +106,24 @@ if (MSVC)
# ZSTD # ZSTD
find_package(zstd CONFIG REQUIRED) find_package(zstd CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC zstd::libzstd_shared) target_link_libraries(sdrpp_core PUBLIC zstd::libzstd_shared)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
../android/deps/volk/jni
../android/deps/fftw3/jni
/mnt/android_sdr/zstd/lib
${ANDROID_NDK}/sources/android/native_app_glue
)
target_link_libraries(sdrpp_core PUBLIC
${PROJECT_SOURCE_DIR}/../android/deps/volk/${ANDROID_ABI}/libcpu_features.a
${PROJECT_SOURCE_DIR}/../android/deps/volk/${ANDROID_ABI}/libvolk.so
${PROJECT_SOURCE_DIR}/../android/deps/fftw3/${ANDROID_ABI}/libfftw3f.a
/mnt/android_sdr/output/zstd/${ANDROID_ABI}/libzstd.so
android
EGL
GLESv3
log
)
else() else()
find_package(PkgConfig) find_package(PkgConfig)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)

View File

@ -22,3 +22,6 @@
#define KB_KEY_RCTRL GLFW_KEY_RIGHT_CONTROL #define KB_KEY_RCTRL GLFW_KEY_RIGHT_CONTROL
#define KB_KEY_LSHIFT GLFW_KEY_LEFT_SHIFT #define KB_KEY_LSHIFT GLFW_KEY_LEFT_SHIFT
#define KB_KEY_RSHIFT GLFW_KEY_RIGHT_SHIFT #define KB_KEY_RSHIFT GLFW_KEY_RIGHT_SHIFT
#define KB_KEY_A GLFW_KEY_A
#define KB_KEY_R GLFW_KEY_R

View File

@ -2,7 +2,7 @@
#include <string> #include <string>
namespace backend { namespace backend {
int init(std::string resDir); int init(std::string resDir = "");
void beginFrame(); void beginFrame();
void render(bool vsync = true); void render(bool vsync = true);
void getMouseScreenPos(double& x, double& y); void getMouseScreenPos(double& x, double& y);

View File

@ -1,7 +1,5 @@
#include <server.h> #include <server.h>
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h> #include <stdio.h>
#include <gui/main_window.h> #include <gui/main_window.h>
#include <gui/style.h> #include <gui/style.h>
@ -188,6 +186,7 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["theme"] = "Dark"; defConfig["theme"] = "Dark";
defConfig["modules"] = json::array(); defConfig["modules"] = json::array();
defConfig["offsetMode"] = (int)0; // Off defConfig["offsetMode"] = (int)0; // Off
defConfig["offset"] = 0.0; defConfig["offset"] = 0.0;
defConfig["showMenu"] = true; defConfig["showMenu"] = true;
@ -213,6 +212,9 @@ int sdrpp_main(int argc, char* argv[]) {
#elif defined(IS_MACOS_BUNDLE) #elif defined(IS_MACOS_BUNDLE)
defConfig["modulesDirectory"] = "../Plugins"; defConfig["modulesDirectory"] = "../Plugins";
defConfig["resourcesDirectory"] = "../Resources"; defConfig["resourcesDirectory"] = "../Resources";
#elif defined(__ANDROID__)
defConfig["modulesDirectory"] = options::opts.root + "/modules";
defConfig["resourcesDirectory"] = options::opts.root + "/res";
#else #else
defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins"; defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins";
defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp"; defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp";
@ -223,8 +225,33 @@ int sdrpp_main(int argc, char* argv[]) {
core::configManager.setPath(options::opts.root + "/config.json"); core::configManager.setPath(options::opts.root + "/config.json");
core::configManager.load(defConfig); core::configManager.load(defConfig);
core::configManager.enableAutoSave(); core::configManager.enableAutoSave();
core::configManager.acquire(); core::configManager.acquire();
// Android can't load just any .so file. This means we have to hardcode the name of the modules
#ifdef __ANDROID__
int modCount = 0;
core::configManager.conf["modules"] = json::array();
core::configManager.conf["modules"][modCount++] = "airspy_source.so";
core::configManager.conf["modules"][modCount++] = "airspyhf_source.so";
core::configManager.conf["modules"][modCount++] = "hackrf_source.so";
core::configManager.conf["modules"][modCount++] = "plutosdr_source.so";
core::configManager.conf["modules"][modCount++] = "sdrpp_server_source.so";
core::configManager.conf["modules"][modCount++] = "rfspace_source.so";
core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so";
core::configManager.conf["modules"][modCount++] = "rtl_tcp_source.so";
core::configManager.conf["modules"][modCount++] = "spyserver_source.so";
core::configManager.conf["modules"][modCount++] = "network_sink.so";
core::configManager.conf["modules"][modCount++] = "audio_sink.so";
core::configManager.conf["modules"][modCount++] = "radio.so";
core::configManager.conf["modules"][modCount++] = "frequency_manager.so";
core::configManager.conf["modules"][modCount++] = "recorder.so";
core::configManager.conf["modules"][modCount++] = "rigctl_server.so";
#endif
// Fix missing elements in config // Fix missing elements in config
for (auto const& item : defConfig.items()) { for (auto const& item : defConfig.items()) {
if (!core::configManager.conf.contains(item.key())) { if (!core::configManager.conf.contains(item.key())) {
@ -274,6 +301,7 @@ int sdrpp_main(int argc, char* argv[]) {
if (!style::loadFonts(resDir)) { return -1; } if (!style::loadFonts(resDir)) { return -1; }
thememenu::init(resDir); thememenu::init(resDir);
LoadingScreen::init();
LoadingScreen::show("Loading icons"); LoadingScreen::show("Loading icons");
spdlog::info("Loading icons"); spdlog::info("Loading icons");
@ -294,6 +322,8 @@ int sdrpp_main(int argc, char* argv[]) {
// Run render loop (TODO: CHECK RETURN VALUE) // Run render loop (TODO: CHECK RETURN VALUE)
backend::renderLoop(); backend::renderLoop();
// On android, none of this shutdown should happen due to the way the UI works
#ifndef __ANDROID__
// Shut down all modules // Shut down all modules
for (auto& [name, mod] : core::moduleManager.modules) { for (auto& [name, mod] : core::moduleManager.modules) {
mod.end(); mod.end();
@ -306,6 +336,7 @@ int sdrpp_main(int argc, char* argv[]) {
core::configManager.disableAutoSave(); core::configManager.disableAutoSave();
core::configManager.save(); core::configManager.save();
#endif
return 0; return 0;
} }

View File

@ -8,8 +8,10 @@
namespace credits { namespace credits {
ImFont* bigFont; ImFont* bigFont;
ImVec2 imageSize(128.0f, 128.0f);
void init() { void init() {
imageSize = ImVec2(128.0f * style::uiScale, 128.0f * style::uiScale);
} }
void show() { void show() {
@ -25,7 +27,7 @@ namespace credits {
ImGui::TextUnformatted("SDR++ "); ImGui::TextUnformatted("SDR++ ");
ImGui::PopFont(); ImGui::PopFont();
ImGui::SameLine(); ImGui::SameLine();
ImGui::Image(icons::LOGO, ImVec2(128, 128)); ImGui::Image(icons::LOGO, imageSize);
ImGui::Spacing(); ImGui::Spacing();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Spacing(); ImGui::Spacing();

View File

@ -1,8 +1,6 @@
#include <gui/dialogs/loading_screen.h> #include <gui/dialogs/loading_screen.h>
#include <gui/main_window.h> #include <gui/main_window.h>
#include <imgui.h> #include <imgui.h>
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <gui/icons.h> #include <gui/icons.h>
#include <gui/style.h> #include <gui/style.h>
#include <credits.h> #include <credits.h>
@ -10,6 +8,11 @@
#include <backend.h> #include <backend.h>
namespace LoadingScreen { namespace LoadingScreen {
ImVec2 imageSize(128.0f, 128.0f);
void init() {
imageSize = ImVec2(128.0f * style::uiScale, 128.0f * style::uiScale);
}
void show(std::string msg) { void show(std::string msg) {
backend::beginFrame(); backend::beginFrame();
@ -26,7 +29,7 @@ namespace LoadingScreen {
ImGui::TextUnformatted("SDR++ "); ImGui::TextUnformatted("SDR++ ");
ImGui::PopFont(); ImGui::PopFont();
ImGui::SameLine(); ImGui::SameLine();
ImGui::Image(icons::LOGO, ImVec2(128, 128)); ImGui::Image(icons::LOGO, imageSize);
ImVec2 origPos = ImGui::GetCursorPos(); ImVec2 origPos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(origPos.y + 50); ImGui::SetCursorPosY(origPos.y + 50);

View File

@ -4,5 +4,6 @@
#include <mutex> #include <mutex>
namespace LoadingScreen { namespace LoadingScreen {
void init();
void show(std::string msg); void show(std::string msg);
}; };

View File

@ -1,8 +1,6 @@
#include <gui/main_window.h> #include <gui/main_window.h>
#include <gui/gui.h> #include <gui/gui.h>
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h> #include <stdio.h>
#include <thread> #include <thread>
#include <complex> #include <complex>
@ -126,10 +124,14 @@ void MainWindow::init() {
// Load additional modules specified through config // Load additional modules specified through config
for (auto const& path : modules) { for (auto const& path : modules) {
#ifndef __ANDROID__
std::string apath = std::filesystem::absolute(path).string(); std::string apath = std::filesystem::absolute(path).string();
spdlog::info("Loading {0}", apath); spdlog::info("Loading {0}", apath);
LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string()); LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string());
core::moduleManager.loadModule(apath); core::moduleManager.loadModule(apath);
#else
core::moduleManager.loadModule(path);
#endif
} }
// Create module instances // Create module instances
@ -354,8 +356,9 @@ void MainWindow::draw() {
} }
// To Bar // To Bar
ImVec2 btnSize(30 * style::uiScale, 30 * style::uiScale);
ImGui::PushID(ImGui::GetID("sdrpp_menu_btn")); ImGui::PushID(ImGui::GetID("sdrpp_menu_btn"));
if (ImGui::ImageButton(icons::MENU, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_MENU, false)) { if (ImGui::ImageButton(icons::MENU, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_MENU, false)) {
showMenu = !showMenu; showMenu = !showMenu;
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["showMenu"] = showMenu; core::configManager.conf["showMenu"] = showMenu;
@ -369,14 +372,14 @@ void MainWindow::draw() {
if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); } if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); }
if (playing) { if (playing) {
ImGui::PushID(ImGui::GetID("sdrpp_stop_btn")); ImGui::PushID(ImGui::GetID("sdrpp_stop_btn"));
if (ImGui::ImageButton(icons::STOP, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) { if (ImGui::ImageButton(icons::STOP, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) {
setPlayState(false); setPlayState(false);
} }
ImGui::PopID(); ImGui::PopID();
} }
else { // TODO: Might need to check if there even is a device else { // TODO: Might need to check if there even is a device
ImGui::PushID(ImGui::GetID("sdrpp_play_btn")); ImGui::PushID(ImGui::GetID("sdrpp_play_btn"));
if (ImGui::ImageButton(icons::PLAY, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) { if (ImGui::ImageButton(icons::PLAY, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(KB_KEY_END, false)) {
setPlayState(true); setPlayState(true);
} }
ImGui::PopID(); ImGui::PopID();
@ -384,20 +387,21 @@ void MainWindow::draw() {
if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); } if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); }
ImGui::SameLine(); ImGui::SameLine();
float origY = ImGui::GetCursorPosY();
//ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8); sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248 * style::uiScale, btnSize.x, 5, true);
sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248, 30, 5, true);
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetCursorPosY(origY);
gui::freqSelect.draw(); gui::freqSelect.draw();
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9); ImGui::SetCursorPosY(origY);
if (tuningMode == tuner::TUNER_MODE_CENTER) { if (tuningMode == tuner::TUNER_MODE_CENTER) {
ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn")); ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn"));
if (ImGui::ImageButton(icons::CENTER_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) { if (ImGui::ImageButton(icons::CENTER_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_NORMAL; tuningMode = tuner::TUNER_MODE_NORMAL;
gui::waterfall.VFOMoveSingleClick = false; gui::waterfall.VFOMoveSingleClick = false;
core::configManager.acquire(); core::configManager.acquire();
@ -408,7 +412,7 @@ void MainWindow::draw() {
} }
else { // TODO: Might need to check if there even is a device else { // TODO: Might need to check if there even is a device
ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn")); ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn"));
if (ImGui::ImageButton(icons::NORMAL_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) { if (ImGui::ImageButton(icons::NORMAL_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_CENTER; tuningMode = tuner::TUNER_MODE_CENTER;
gui::waterfall.VFOMoveSingleClick = true; gui::waterfall.VFOMoveSingleClick = true;
tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency); tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency);
@ -421,19 +425,20 @@ void MainWindow::draw() {
ImGui::SameLine(); ImGui::SameLine();
int snrWidth = std::min<int>(300, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - 87); int snrOffset = 87.0f * style::uiScale;
int snrWidth = std::min<int>(300.0f * style::uiScale, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - snrOffset);
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth + 87)); ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth + snrOffset));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); ImGui::SetCursorPosY(origY + (5.0f * style::uiScale));
ImGui::SetNextItemWidth(snrWidth); ImGui::SetNextItemWidth(snrWidth);
ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0); ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0);
ImGui::SameLine(); ImGui::SameLine();
// Logo button // Logo button
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48); ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (48 * style::uiScale));
ImGui::SetCursorPosY(10); ImGui::SetCursorPosY(10 * style::uiScale);
if (ImGui::ImageButton(icons::LOGO, ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), 0)) { if (ImGui::ImageButton(icons::LOGO, ImVec2(32 * style::uiScale, 32 * style::uiScale), ImVec2(0, 0), ImVec2(1, 1), 0)) {
showCredits = true; showCredits = true;
} }
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
@ -455,7 +460,7 @@ void MainWindow::draw() {
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250); newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive)); ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
} }
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) { if (mousePos.x >= newWidth - (2.0f * style::uiScale) && mousePos.x <= newWidth + (2.0f * style::uiScale) && mousePos.y > curY) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (click) { if (click) {
grabbingMenu = true; grabbingMenu = true;
@ -479,8 +484,8 @@ void MainWindow::draw() {
if (showMenu) { if (showMenu) {
ImGui::Columns(3, "WindowColumns", false); ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, menuWidth); ImGui::SetColumnWidth(0, menuWidth);
ImGui::SetColumnWidth(1, winSize.x - menuWidth - 60); ImGui::SetColumnWidth(1, winSize.x - menuWidth - (60.0f * style::uiScale));
ImGui::SetColumnWidth(2, 60); ImGui::SetColumnWidth(2, 60.0f * style::uiScale);
ImGui::BeginChild("Left Column"); ImGui::BeginChild("Left Column");
if (gui::menu.draw(firstMenuRender)) { if (gui::menu.draw(firstMenuRender)) {
@ -513,7 +518,7 @@ void MainWindow::draw() {
ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency()); ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency());
ImGui::Text("Source name: %s", sourceName.c_str()); ImGui::Text("Source name: %s", sourceName.c_str());
ImGui::Checkbox("Show demo window", &demoWindow); ImGui::Checkbox("Show demo window", &demoWindow);
ImGui::Text("ImGui version: %s", ImGui::GetVersion()); ImGui::Text("ImGui version: %s_feb_2022", ImGui::GetVersion());
ImGui::Checkbox("Bypass buffering", &sigpath::signalPath.inputBuffer.bypass); ImGui::Checkbox("Bypass buffering", &sigpath::signalPath.inputBuffer.bypass);
@ -538,9 +543,9 @@ void MainWindow::draw() {
else { else {
// When hiding the menu bar // When hiding the menu bar
ImGui::Columns(3, "WindowColumns", false); ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, 8); ImGui::SetColumnWidth(0, 8 * style::uiScale);
ImGui::SetColumnWidth(1, winSize.x - 8 - 60); ImGui::SetColumnWidth(1, winSize.x - ((8 + 60) * style::uiScale));
ImGui::SetColumnWidth(2, 60); ImGui::SetColumnWidth(2, 60.0f * style::uiScale);
} }
// Right Column // Right Column
@ -602,8 +607,9 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0)); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0));
ImGui::TextUnformatted("Zoom"); ImGui::TextUnformatted("Zoom");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, 1.0, 0.0, "")) { ImVec2 wfSliderSize(20.0 * style::uiScale, 150.0 * style::uiScale);
if (ImGui::VSliderFloat("##_7_", wfSliderSize, &bw, 1.0, 0.0, "")) {
double factor = (double)bw * (double)bw; double factor = (double)bw * (double)bw;
// Map 0.0 -> 1.0 to 1000.0 -> bandwidth // Map 0.0 -> 1.0 to 1000.0 -> bandwidth
@ -621,8 +627,8 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0)); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0));
ImGui::TextUnformatted("Max"); ImGui::TextUnformatted("Max");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -160.0f, "")) { if (ImGui::VSliderFloat("##_8_", wfSliderSize, &fftMax, 0.0, -160.0f, "")) {
fftMax = std::max<float>(fftMax, fftMin + 10); fftMax = std::max<float>(fftMax, fftMin + 10);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["max"] = fftMax; core::configManager.conf["max"] = fftMax;
@ -633,8 +639,8 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0)); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0));
ImGui::TextUnformatted("Min"); ImGui::TextUnformatted("Min");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -160.0f, "")) { if (ImGui::VSliderFloat("##_9_", wfSliderSize, &fftMin, 0.0, -160.0f, "")) {
fftMin = std::min<float>(fftMax - 10, fftMin); fftMin = std::min<float>(fftMax - 10, fftMin);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["min"] = fftMin; core::configManager.conf["min"] = fftMin;
@ -708,3 +714,7 @@ void MainWindow::setFFTWindow(int win) {
bool MainWindow::isPlaying() { bool MainWindow::isPlaying() {
return playing; return playing;
} }
void MainWindow::setFirstMenuRender() {
firstMenuRender = true;
}

View File

@ -19,6 +19,7 @@ public:
bool sdrIsRunning(); bool sdrIsRunning();
void setFFTSize(int size); void setFFTSize(int size);
void setFFTWindow(int win); void setFFTWindow(int win);
void setFirstMenuRender();
// TODO: Replace with it's own class // TODO: Replace with it's own class
void setVFO(double freq); void setVFO(double freq);

View File

@ -23,8 +23,11 @@ namespace thememenu {
it = std::find(themeNames.begin(), themeNames.end(), "Dark"); it = std::find(themeNames.begin(), themeNames.end(), "Dark");
selectedThemeName = "Dark"; selectedThemeName = "Dark";
} }
gui::themeManager.applyTheme(selectedThemeName);
themeId = std::distance(themeNames.begin(), it); themeId = std::distance(themeNames.begin(), it);
applyTheme();
// Apply scaling
ImGui::GetStyle().ScaleAllSizes(style::uiScale);
themeNamesTxt = ""; themeNamesTxt = "";
for (auto name : themeNames) { for (auto name : themeNames) {
@ -33,12 +36,16 @@ namespace thememenu {
} }
} }
void applyTheme() {
gui::themeManager.applyTheme(themeNames[themeId]);
}
void draw(void* ctx) { void draw(void* ctx) {
float menuWidth = ImGui::GetContentRegionAvailWidth(); float menuWidth = ImGui::GetContentRegionAvailWidth();
ImGui::LeftLabel("Theme"); ImGui::LeftLabel("Theme");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##theme_select_combo", &themeId, themeNamesTxt.c_str())) { if (ImGui::Combo("##theme_select_combo", &themeId, themeNamesTxt.c_str())) {
gui::themeManager.applyTheme(themeNames[themeId]); applyTheme();
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["theme"] = themeNames[themeId]; core::configManager.conf["theme"] = themeNames[themeId];
core::configManager.release(true); core::configManager.release(true);

View File

@ -3,5 +3,6 @@
namespace thememenu { namespace thememenu {
void init(std::string resDir); void init(std::string resDir);
void applyTheme();
void draw(void* ctx); void draw(void* ctx);
} }

View File

@ -11,15 +11,21 @@ namespace style {
ImFont* bigFont; ImFont* bigFont;
ImFont* hugeFont; ImFont* hugeFont;
#ifndef __ANDROID__
float uiScale = 1.0f;
#else
float uiScale = 3.0f;
#endif
bool loadFonts(std::string resDir) { bool loadFonts(std::string resDir) {
if (!std::filesystem::is_directory(resDir)) { if (!std::filesystem::is_directory(resDir)) {
spdlog::error("Invalid resource directory: {0}", resDir); spdlog::error("Invalid resource directory: {0}", resDir);
return false; return false;
} }
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f * uiScale);
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f); bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f * uiScale);
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f); hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f * uiScale);
return true; return true;
} }

View File

@ -7,6 +7,8 @@ namespace style {
extern ImFont* bigFont; extern ImFont* bigFont;
extern ImFont* hugeFont; extern ImFont* hugeFont;
extern float uiScale;
bool setDefaultStyle(std::string resDir); bool setDefaultStyle(std::string resDir);
bool loadFonts(std::string resDir); bool loadFonts(std::string resDir);
void beginDisabled(); void beginDisabled();

View File

@ -42,9 +42,6 @@ void FrequencySelect::onPosChange() {
} }
} }
void FrequencySelect::onResize() {
}
void FrequencySelect::incrementDigit(int i) { void FrequencySelect::incrementDigit(int i) {
if (i < 0) { if (i < 0) {
return; return;
@ -93,34 +90,27 @@ void FrequencySelect::moveCursorToDigit(int i) {
} }
void FrequencySelect::draw() { void FrequencySelect::draw() {
window = ImGui::GetCurrentWindow(); auto window = ImGui::GetCurrentWindow();
widgetPos = ImGui::GetWindowContentRegionMin(); widgetPos = ImGui::GetWindowContentRegionMin();
widgetEndPos = ImGui::GetWindowContentRegionMax();
ImVec2 cursorPos = ImGui::GetCursorPos(); ImVec2 cursorPos = ImGui::GetCursorPos();
widgetPos.x += window->Pos.x + cursorPos.x; widgetPos.x += window->Pos.x + cursorPos.x;
widgetPos.y += window->Pos.y - 3;
widgetEndPos.x += window->Pos.x + cursorPos.x;
widgetEndPos.y += window->Pos.y - 3;
widgetSize = ImVec2(widgetEndPos.x - widgetPos.x, widgetEndPos.y - widgetPos.y);
ImGui::PushFont(style::bigFont); ImGui::PushFont(style::bigFont);
ImVec2 digitSz = ImGui::CalcTextSize("0");
ImVec2 commaSz = ImGui::CalcTextSize(".");
widgetPos.y = cursorPos.y - ((digitSz.y / 2.0f) - ceilf(15 * style::uiScale) - 5);
if (widgetPos.x != lastWidgetPos.x || widgetPos.y != lastWidgetPos.y) { if (widgetPos.x != lastWidgetPos.x || widgetPos.y != lastWidgetPos.y) {
lastWidgetPos = widgetPos; lastWidgetPos = widgetPos;
onPosChange(); onPosChange();
} }
if (widgetSize.x != lastWidgetSize.x || widgetSize.y != lastWidgetSize.y) {
lastWidgetSize = widgetSize;
onResize();
}
ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f); ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f);
ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text); ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
ImVec2 digitSz = ImGui::CalcTextSize("0");
ImVec2 commaSz = ImGui::CalcTextSize(".");
int digitWidth = digitSz.x; int digitWidth = digitSz.x;
int commaOffset = 0; int commaOffset = 0;
float textOffset = 11.0f * style::uiScale;
bool zeros = true; bool zeros = true;
ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y))); ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y)));
@ -134,7 +124,7 @@ void FrequencySelect::draw() {
zeros ? disabledColor : textColor, buf); zeros ? disabledColor : textColor, buf);
if ((i + 1) % 3 == 0 && i < 11) { if ((i + 1) % 3 == 0 && i < 11) {
commaOffset += commaSz.x; commaOffset += commaSz.x;
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + 11, widgetPos.y), window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + textOffset, widgetPos.y),
zeros ? disabledColor : textColor, "."); zeros ? disabledColor : textColor, ".");
} }
} }
@ -223,9 +213,7 @@ void FrequencySelect::draw() {
ImGui::PopFont(); ImGui::PopFont();
ImGui::SetCursorPosX(digitBottomMaxs[11].x + 17); ImGui::SetCursorPosX(digitBottomMaxs[11].x + (17.0f * style::uiScale));
//ImGui::NewLine();
} }
void FrequencySelect::setFrequency(int64_t freq) { void FrequencySelect::setFrequency(int64_t freq) {

View File

@ -26,13 +26,7 @@ private:
void moveCursorToDigit(int i); void moveCursorToDigit(int i);
ImVec2 widgetPos; ImVec2 widgetPos;
ImVec2 widgetEndPos;
ImVec2 widgetSize;
ImVec2 lastWidgetPos; ImVec2 lastWidgetPos;
ImVec2 lastWidgetSize;
ImGuiWindow* window;
int digits[12]; int digits[12];
ImVec2 digitBottomMins[12]; ImVec2 digitBottomMins[12];

View File

@ -28,6 +28,7 @@ bool Menu::draw(bool updateStates) {
bool changed = false; bool changed = false;
float menuWidth = ImGui::GetContentRegionAvailWidth(); float menuWidth = ImGui::GetContentRegionAvailWidth();
ImGuiWindow* window = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::GetCurrentWindow();
ImVec2 checkboxOffset = ImVec2(menuWidth - ImGui::GetTextLineHeight() - (6.0f * style::uiScale), - ImGui::GetTextLineHeight() - (10.0f * style::uiScale));
int displayedCount = 0; int displayedCount = 0;
int rawId = 0; int rawId = 0;
@ -46,6 +47,7 @@ bool Menu::draw(bool updateStates) {
continue; continue;
} }
// Draw dragged menu item
if (displayedCount == insertBefore && !draggedMenuName.empty()) { if (displayedCount == insertBefore && !draggedMenuName.empty()) {
if (updateStates) { ImGui::SetNextItemOpen(false); } if (updateStates) { ImGui::SetNextItemOpen(false); }
ImVec2 posMin = ImGui::GetCursorScreenPos(); ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -56,14 +58,14 @@ bool Menu::draw(bool updateStates) {
if (items[draggedOpt.name].inst != NULL) { if (items[draggedOpt.name].inst != NULL) {
window->WorkRect = orignalRect; window->WorkRect = orignalRect;
ImVec2 pos = ImGui::GetCursorPos(); ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = items[draggedOpt.name].inst->isEnabled(); bool enabled = items[draggedOpt.name].inst->isEnabled();
ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled); ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled);
ImGui::SetCursorPos(pos); ImGui::SetCursorPos(pos);
} }
style::endDisabled(); style::endDisabled();
window->DrawList->AddRect(posMin, posMax, textColor); window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale);
} }
displayedCount++; displayedCount++;
@ -72,7 +74,7 @@ bool Menu::draw(bool updateStates) {
ImRect orginalRect = window->WorkRect; ImRect orginalRect = window->WorkRect;
if (item.inst != NULL) { if (item.inst != NULL) {
window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - 6, orginalRect.Max.y)); window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - (6.0f * style::uiScale), orginalRect.Max.y));
} }
ImVec2 posMin = ImGui::GetCursorScreenPos(); ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -93,13 +95,14 @@ bool Menu::draw(bool updateStates) {
continue; continue;
} }
// Draw menu header and menu content. There is a lot of boilerplate because the checkbox has to be drawn before the menu, TODO: fix
if (updateStates) { ImGui::SetNextItemOpen(opt.open); } if (updateStates) { ImGui::SetNextItemOpen(opt.open); }
if (ImGui::CollapsingHeader((opt.name + "##sdrpp_main_menu").c_str())) { if (ImGui::CollapsingHeader((opt.name + "##sdrpp_main_menu").c_str())) {
if (item.inst != NULL) { if (item.inst != NULL) {
window->WorkRect = orginalRect; window->WorkRect = orginalRect;
ImVec2 pos = ImGui::GetCursorPos(); ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = item.inst->isEnabled(); bool enabled = item.inst->isEnabled();
if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) {
enabled ? item.inst->enable() : item.inst->disable(); enabled ? item.inst->enable() : item.inst->disable();
@ -120,8 +123,8 @@ bool Menu::draw(bool updateStates) {
else if (item.inst != NULL) { else if (item.inst != NULL) {
window->WorkRect = orginalRect; window->WorkRect = orginalRect;
ImVec2 pos = ImGui::GetCursorPos(); ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = item.inst->isEnabled(); bool enabled = item.inst->isEnabled();
if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) {
enabled ? item.inst->enable() : item.inst->disable(); enabled ? item.inst->enable() : item.inst->disable();
@ -168,7 +171,7 @@ bool Menu::draw(bool updateStates) {
insertBefore = -1; insertBefore = -1;
} }
// TODO: Figure out why the hell this is needed
if (insertBefore == displayedCount && !draggedMenuName.empty()) { if (insertBefore == displayedCount && !draggedMenuName.empty()) {
if (updateStates) { ImGui::SetNextItemOpen(false); } if (updateStates) { ImGui::SetNextItemOpen(false); }
ImVec2 posMin = ImGui::GetCursorScreenPos(); ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -179,14 +182,14 @@ bool Menu::draw(bool updateStates) {
if (items[draggedOpt.name].inst != NULL) { if (items[draggedOpt.name].inst != NULL) {
window->WorkRect = orignalRect; window->WorkRect = orignalRect;
ImVec2 pos = ImGui::GetCursorPos(); ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = items[draggedOpt.name].inst->isEnabled(); bool enabled = items[draggedOpt.name].inst->isEnabled();
ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled); ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled);
ImGui::SetCursorPos(pos); ImGui::SetCursorPos(pos);
} }
style::endDisabled(); style::endDisabled();
window->DrawList->AddRect(posMin, posMax, textColor); window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale);
} }
if (!draggedMenuName.empty()) { if (!draggedMenuName.empty()) {

View File

@ -1,5 +1,6 @@
#include <gui/widgets/volume_meter.h> #include <gui/widgets/volume_meter.h>
#include <algorithm> #include <algorithm>
#include <gui/style.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS #ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
@ -27,15 +28,15 @@ namespace ImGui {
float it = size.x / 9; float it = size.x / 9;
char buf[32]; char buf[32];
window->DrawList->AddRectFilled(min + ImVec2(0, 1), min + ImVec2(roundf((float)val * ratio), 10), IM_COL32(0, 136, 255, 255)); window->DrawList->AddRectFilled(min + ImVec2(0, 1), min + ImVec2(roundf((float)val * ratio), 10 * style::uiScale), IM_COL32(0, 136, 255, 255));
window->DrawList->AddLine(min, min + ImVec2(0, 9), text); window->DrawList->AddLine(min, min + ImVec2(0, (10.0f * style::uiScale) - 1), text, style::uiScale);
window->DrawList->AddLine(min + ImVec2(0, 9), min + ImVec2(size.x + 1, 9), text); window->DrawList->AddLine(min + ImVec2(0, (10.0f * style::uiScale) - 1), min + ImVec2(size.x + 1, (10.0f * style::uiScale) - 1), text, style::uiScale);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
window->DrawList->AddLine(min + ImVec2(roundf((float)i * it), 9), min + ImVec2(roundf((float)i * it), 14), text); window->DrawList->AddLine(min + ImVec2(roundf((float)i * it), (10.0f * style::uiScale) - 1), min + ImVec2(roundf((float)i * it), (15.0f * style::uiScale) - 1), text, style::uiScale);
sprintf(buf, "%d", i * 10); sprintf(buf, "%d", i * 10);
ImVec2 sz = ImGui::CalcTextSize(buf); ImVec2 sz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(min + ImVec2(roundf(((float)i * it) - (sz.x / 2.0)) + 1, 16), text, buf); window->DrawList->AddText(min + ImVec2(roundf(((float)i * it) - (sz.x / 2.0)) + 1, 16.0f * style::uiScale), text, buf);
} }
} }
} }

View File

@ -6,6 +6,7 @@
#include <volk/volk.h> #include <volk/volk.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/style.h>
#include <keybinds.h> #include <keybinds.h>
float DEFAULT_COLOR_MAP[][3] = { float DEFAULT_COLOR_MAP[][3] = {
@ -99,51 +100,53 @@ namespace ImGui {
ImU32 trace = ImGui::GetColorU32(ImGuiCol_PlotLines); ImU32 trace = ImGui::GetColorU32(ImGuiCol_PlotLines);
ImU32 shadow = ImGui::GetColorU32(ImGuiCol_PlotLines, 0.2); ImU32 shadow = ImGui::GetColorU32(ImGuiCol_PlotLines, 0.2);
ImU32 text = ImGui::GetColorU32(ImGuiCol_Text); ImU32 text = ImGui::GetColorU32(ImGuiCol_Text);
float textVOffset = 10.0f * style::uiScale;
// Vertical scale // Vertical scale
for (float line = startLine; line > fftMin; line -= vRange) { for (float line = startLine; line > fftMin; line -= vRange) {
float yPos = widgetPos.y + fftHeight + 10 - ((line - fftMin) * scaleFactor); float yPos = fftAreaMax.y - ((line - fftMin) * scaleFactor);
window->DrawList->AddLine(ImVec2(roundf(widgetPos.x + 50), roundf(yPos)), window->DrawList->AddLine(ImVec2(fftAreaMin.x, roundf(yPos)),
ImVec2(roundf(widgetPos.x + dataWidth + 50), roundf(yPos)), ImVec2(fftAreaMax.x, roundf(yPos)),
IM_COL32(50, 50, 50, 255), 1.0); IM_COL32(50, 50, 50, 255), style::uiScale);
sprintf(buf, "%d", (int)line); sprintf(buf, "%d", (int)line);
ImVec2 txtSz = ImGui::CalcTextSize(buf); ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(widgetPos.x + 40 - txtSz.x, roundf(yPos - (txtSz.y / 2.0))), text, buf); window->DrawList->AddText(ImVec2(fftAreaMin.x - txtSz.x - textVOffset, roundf(yPos - (txtSz.y / 2.0))), text, buf);
} }
// Horizontal scale // Horizontal scale
double startFreq = ceilf(lowerFreq / range) * range; double startFreq = ceilf(lowerFreq / range) * range;
double horizScale = (double)dataWidth / viewBandwidth; double horizScale = (double)dataWidth / viewBandwidth;
float scaleVOfsset = 7 * style::uiScale;
for (double freq = startFreq; freq < upperFreq; freq += range) { for (double freq = startFreq; freq < upperFreq; freq += range) {
double xPos = widgetPos.x + 50 + ((freq - lowerFreq) * horizScale); double xPos = fftAreaMin.x + ((freq - lowerFreq) * horizScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + 10), window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMin.y + 1),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10), ImVec2(roundf(xPos), fftAreaMax.y),
IM_COL32(50, 50, 50, 255), 1.0); IM_COL32(50, 50, 50, 255), style::uiScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10), window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMax.y),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 17), ImVec2(roundf(xPos), fftAreaMax.y + scaleVOfsset),
text, 1.0); text, style::uiScale);
printAndScale(freq, buf); printAndScale(freq, buf);
ImVec2 txtSz = ImGui::CalcTextSize(buf); ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0)), widgetPos.y + fftHeight + 10 + txtSz.y), text, buf); window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0)), fftAreaMax.y + txtSz.y), text, buf);
} }
// Data // Data
if (latestFFT != NULL && fftLines != 0) { if (latestFFT != NULL && fftLines != 0) {
for (int i = 1; i < dataWidth; i++) { for (int i = 1; i < dataWidth; i++) {
double aPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i - 1] - fftMin) * scaleFactor); double aPos = fftAreaMax.y - ((latestFFT[i - 1] - fftMin) * scaleFactor);
double bPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i] - fftMin) * scaleFactor); double bPos = fftAreaMax.y - ((latestFFT[i] - fftMin) * scaleFactor);
aPos = std::clamp<double>(aPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10); aPos = std::clamp<double>(aPos, fftAreaMin.y + 1, fftAreaMax.y);
bPos = std::clamp<double>(bPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10); bPos = std::clamp<double>(bPos, fftAreaMin.y + 1, fftAreaMax.y);
window->DrawList->AddLine(ImVec2(widgetPos.x + 49 + i, roundf(aPos)), window->DrawList->AddLine(ImVec2(fftAreaMin.x + i - 1, roundf(aPos)),
ImVec2(widgetPos.x + 50 + i, roundf(bPos)), trace, 1.0); ImVec2(fftAreaMin.x + i, roundf(bPos)), trace, 1.0);
window->DrawList->AddLine(ImVec2(widgetPos.x + 50 + i, roundf(bPos)), window->DrawList->AddLine(ImVec2(fftAreaMin.x + i, roundf(bPos)),
ImVec2(widgetPos.x + 50 + i, widgetPos.y + fftHeight + 10), shadow, 1.0); ImVec2(fftAreaMin.x + i, fftAreaMax.y), shadow, 1.0);
} }
} }
FFTRedrawArgs args; FFTRedrawArgs args;
args.min = ImVec2(widgetPos.x + 50, widgetPos.y + 9); args.min = fftAreaMin;
args.max = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10); args.max = fftAreaMax;
args.lowFreq = lowerFreq; args.lowFreq = lowerFreq;
args.highFreq = upperFreq; args.highFreq = upperFreq;
args.freqToPixelRatio = horizScale; args.freqToPixelRatio = horizScale;
@ -152,13 +155,13 @@ namespace ImGui {
onFFTRedraw.emit(args); onFFTRedraw.emit(args);
// X Axis // X Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 10), window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMax.y),
ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10), ImVec2(fftAreaMax.x, fftAreaMax.y),
text, 1.0); text, style::uiScale);
// Y Axis // Y Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + 9), window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMin.y),
ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 9), ImVec2(fftAreaMin.x, fftAreaMax.y - 1),
text, 1.0); text, style::uiScale);
} }
void WaterFall::drawWaterfall() { void WaterFall::drawWaterfall() {
@ -173,7 +176,7 @@ namespace ImGui {
for (auto const& [name, vfo] : vfos) { for (auto const& [name, vfo] : vfos) {
window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, vfo->color); window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, vfo->color);
if (!vfo->lineVisible) { continue; } if (!vfo->lineVisible) { continue; }
window->DrawList->AddLine(vfo->wfLineMin, vfo->wfLineMax, (name == selectedVFO) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255)); window->DrawList->AddLine(vfo->wfLineMin, vfo->wfLineMax, (name == selectedVFO) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255), style::uiScale);
} }
} }
} }
@ -212,7 +215,7 @@ namespace ImGui {
bool mouseClicked = ImGui::ButtonBehavior(ImRect(fftAreaMin, wfMax), GetID("WaterfallID"), &mouseHovered, &mouseHeld, bool mouseClicked = ImGui::ButtonBehavior(ImRect(fftAreaMin, wfMax), GetID("WaterfallID"), &mouseHovered, &mouseHeld,
ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_PressedOnClick); ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_PressedOnClick);
mouseInFFTResize = (dragOrigin.x > widgetPos.x && dragOrigin.x < widgetPos.x + widgetSize.x && dragOrigin.y >= widgetPos.y + newFFTAreaHeight - 2 && dragOrigin.y <= widgetPos.y + newFFTAreaHeight + 2); mouseInFFTResize = (dragOrigin.x > widgetPos.x && dragOrigin.x < widgetPos.x + widgetSize.x && dragOrigin.y >= widgetPos.y + newFFTAreaHeight - (2.0f * style::uiScale) && dragOrigin.y <= widgetPos.y + newFFTAreaHeight + (2.0f * style::uiScale));
mouseInFreq = IS_IN_AREA(dragOrigin, freqAreaMin, freqAreaMax); mouseInFreq = IS_IN_AREA(dragOrigin, freqAreaMin, freqAreaMax);
mouseInFFT = IS_IN_AREA(dragOrigin, fftAreaMin, fftAreaMax); mouseInFFT = IS_IN_AREA(dragOrigin, fftAreaMin, fftAreaMax);
mouseInWaterfall = IS_IN_AREA(dragOrigin, wfMin, wfMax); mouseInWaterfall = IS_IN_AREA(dragOrigin, wfMin, wfMax);
@ -304,7 +307,7 @@ namespace ImGui {
newFFTAreaHeight = mousePos.y - widgetPos.y; newFFTAreaHeight = mousePos.y - widgetPos.y;
newFFTAreaHeight = std::clamp<float>(newFFTAreaHeight, 150, widgetSize.y - 50); newFFTAreaHeight = std::clamp<float>(newFFTAreaHeight, 150, widgetSize.y - 50);
ImGui::GetForegroundDrawList()->AddLine(ImVec2(widgetPos.x, newFFTAreaHeight + widgetPos.y), ImVec2(widgetEndPos.x, newFFTAreaHeight + widgetPos.y), ImGui::GetForegroundDrawList()->AddLine(ImVec2(widgetPos.x, newFFTAreaHeight + widgetPos.y), ImVec2(widgetEndPos.x, newFFTAreaHeight + widgetPos.y),
ImGui::GetColorU32(ImGuiCol_SeparatorActive)); ImGui::GetColorU32(ImGuiCol_SeparatorActive), style::uiScale);
return; return;
} }
@ -417,7 +420,7 @@ namespace ImGui {
// Finally, if nothing else was selected, just move the VFO // Finally, if nothing else was selected, just move the VFO
if ((VFOMoveSingleClick ? ImGui::IsMouseClicked(ImGuiMouseButton_Left) : ImGui::IsMouseDown(ImGuiMouseButton_Left)) && (mouseInFFT | mouseInWaterfall) && (mouseMoved || hoveredVFOName == "")) { if ((VFOMoveSingleClick ? ImGui::IsMouseClicked(ImGuiMouseButton_Left) : ImGui::IsMouseDown(ImGuiMouseButton_Left)) && (mouseInFFT | mouseInWaterfall) && (mouseMoved || hoveredVFOName == "")) {
if (selVfo != NULL) { if (selVfo != NULL) {
int refCenter = mousePos.x - (widgetPos.x + 50); int refCenter = mousePos.x - fftAreaMin.x;
if (refCenter >= 0 && refCenter < dataWidth) { if (refCenter >= 0 && refCenter < dataWidth) {
double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset; double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset;
off += centerFreq; off += centerFreq;
@ -599,10 +602,10 @@ namespace ImGui {
float bpBottom; float bpBottom;
if (bandPlanPos == BANDPLAN_POS_BOTTOM) { if (bandPlanPos == BANDPLAN_POS_BOTTOM) {
bpBottom = widgetPos.y + fftHeight + 10; bpBottom = fftAreaMax.y;
} }
else { else {
bpBottom = widgetPos.y + height + 10; bpBottom = fftAreaMin.y + height + 1;
} }
@ -620,9 +623,9 @@ namespace ImGui {
start = std::clamp<double>(start, lowerFreq, upperFreq); start = std::clamp<double>(start, lowerFreq, upperFreq);
end = std::clamp<double>(end, lowerFreq, upperFreq); end = std::clamp<double>(end, lowerFreq, upperFreq);
center = (start + end) / 2.0; center = (start + end) / 2.0;
aPos = widgetPos.x + 50 + ((start - lowerFreq) * horizScale); aPos = fftAreaMin.x + ((start - lowerFreq) * horizScale);
bPos = widgetPos.x + 50 + ((end - lowerFreq) * horizScale); bPos = fftAreaMin.x + ((end - lowerFreq) * horizScale);
cPos = widgetPos.x + 50 + ((center - lowerFreq) * horizScale); cPos = fftAreaMin.x + ((center - lowerFreq) * horizScale);
width = bPos - aPos; width = bPos - aPos;
txtSz = ImGui::CalcTextSize(bandplan->bands[i].name.c_str()); txtSz = ImGui::CalcTextSize(bandplan->bands[i].name.c_str());
if (bandplan::colorTable.find(bandplan->bands[i].type.c_str()) != bandplan::colorTable.end()) { if (bandplan::colorTable.find(bandplan->bands[i].type.c_str()) != bandplan::colorTable.end()) {
@ -633,22 +636,22 @@ namespace ImGui {
color = IM_COL32(255, 255, 255, 255); color = IM_COL32(255, 255, 255, 255);
colorTrans = IM_COL32(255, 255, 255, 100); colorTrans = IM_COL32(255, 255, 255, 100);
} }
if (aPos <= widgetPos.x + 50) { if (aPos <= fftAreaMin.x) {
aPos = widgetPos.x + 51; aPos = fftAreaMin.x + 1;
} }
if (bPos <= widgetPos.x + 50) { if (bPos <= fftAreaMin.x) {
bPos = widgetPos.x + 51; bPos = fftAreaMin.x + 1;
} }
if (width >= 1.0) { if (width >= 1.0) {
window->DrawList->AddRectFilled(ImVec2(roundf(aPos), bpBottom - height), window->DrawList->AddRectFilled(ImVec2(roundf(aPos), bpBottom - height),
ImVec2(roundf(bPos), bpBottom), colorTrans); ImVec2(roundf(bPos), bpBottom), colorTrans);
if (startVis) { if (startVis) {
window->DrawList->AddLine(ImVec2(roundf(aPos), bpBottom - height - 1), window->DrawList->AddLine(ImVec2(roundf(aPos), bpBottom - height - 1),
ImVec2(roundf(aPos), bpBottom - 1), color); ImVec2(roundf(aPos), bpBottom - 1), color, style::uiScale);
} }
if (endVis) { if (endVis) {
window->DrawList->AddLine(ImVec2(roundf(bPos), bpBottom - height - 1), window->DrawList->AddLine(ImVec2(roundf(bPos), bpBottom - height - 1),
ImVec2(roundf(bPos), bpBottom - 1), color); ImVec2(roundf(bPos), bpBottom - 1), color, style::uiScale);
} }
} }
if (txtSz.x <= width) { if (txtSz.x <= width) {
@ -679,15 +682,15 @@ namespace ImGui {
int lastWaterfallHeight = waterfallHeight; int lastWaterfallHeight = waterfallHeight;
if (waterfallVisible) { if (waterfallVisible) {
FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - 50); FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - (50.0f * style::uiScale));
newFFTAreaHeight = FFTAreaHeight; newFFTAreaHeight = FFTAreaHeight;
fftHeight = FFTAreaHeight - 50; fftHeight = FFTAreaHeight - (50.0f * style::uiScale);
waterfallHeight = widgetSize.y - fftHeight - 52; waterfallHeight = widgetSize.y - fftHeight - (50.0f * style::uiScale) - 2;
} }
else { else {
fftHeight = widgetSize.y - 50; fftHeight = widgetSize.y - (50.0f * style::uiScale);
} }
dataWidth = widgetSize.x - 60.0; dataWidth = widgetSize.x - (60.0f * style::uiScale);
if (waterfallVisible) { if (waterfallVisible) {
// Raw FFT resize // Raw FFT resize
@ -724,12 +727,14 @@ namespace ImGui {
latestFFT[i] = -1000.0; // Hide everything latestFFT[i] = -1000.0; // Hide everything
} }
fftAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + 9); fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale));
fftAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10); fftAreaMax = ImVec2(fftAreaMin.x + dataWidth, fftAreaMin.y + fftHeight + 1);
freqAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 11);
freqAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 50); freqAreaMin = ImVec2(fftAreaMin.x, fftAreaMax.y + 1);
wfMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 51); freqAreaMax = ImVec2(fftAreaMax.x, fftAreaMax.y + (40.0f * style::uiScale));
wfMax = ImVec2(widgetPos.x + 50 + dataWidth, widgetPos.y + fftHeight + 51 + waterfallHeight);
wfMin = ImVec2(fftAreaMin.x, freqAreaMax.y + 1);
wfMax = ImVec2(fftAreaMin.x + dataWidth, wfMin.y + waterfallHeight);
maxHSteps = dataWidth / (ImGui::CalcTextSize("000.000").x + 10); maxHSteps = dataWidth / (ImGui::CalcTextSize("000.000").x + 10);
maxVSteps = fftHeight / (ImGui::CalcTextSize("000.000").y); maxVSteps = fftHeight / (ImGui::CalcTextSize("000.000").y);
@ -769,8 +774,8 @@ namespace ImGui {
//window->DrawList->AddRectFilled(widgetPos, widgetEndPos, IM_COL32( 0, 0, 0, 255 )); //window->DrawList->AddRectFilled(widgetPos, widgetEndPos, IM_COL32( 0, 0, 0, 255 ));
ImU32 bg = ImGui::ColorConvertFloat4ToU32(gui::themeManager.waterfallBg); ImU32 bg = ImGui::ColorConvertFloat4ToU32(gui::themeManager.waterfallBg);
window->DrawList->AddRectFilled(widgetPos, widgetEndPos, bg); window->DrawList->AddRectFilled(widgetPos, widgetEndPos, bg);
window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255)); window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255), 0.0, 0, style::uiScale);
window->DrawList->AddLine(ImVec2(widgetPos.x, widgetPos.y + fftHeight + 50), ImVec2(widgetPos.x + widgetSize.x, widgetPos.y + fftHeight + 50), IM_COL32(50, 50, 50, 255), 1.0); window->DrawList->AddLine(ImVec2(widgetPos.x, freqAreaMax.y), ImVec2(widgetPos.x + widgetSize.x, freqAreaMax.y), IM_COL32(50, 50, 50, 255), style::uiScale);
if (!gui::mainWindow.lockWaterfallControls) { if (!gui::mainWindow.lockWaterfallControls) {
inputHandled = false; inputHandled = false;
@ -1041,8 +1046,8 @@ namespace ImGui {
vfo->updateDrawingVars(viewBandwidth, dataWidth, viewOffset, widgetPos, fftHeight); vfo->updateDrawingVars(viewBandwidth, dataWidth, viewOffset, widgetPos, fftHeight);
vfo->wfRectMin = ImVec2(vfo->rectMin.x, wfMin.y); vfo->wfRectMin = ImVec2(vfo->rectMin.x, wfMin.y);
vfo->wfRectMax = ImVec2(vfo->rectMax.x, wfMax.y); vfo->wfRectMax = ImVec2(vfo->rectMax.x, wfMax.y);
vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y); vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y - 1);
vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y); vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y - 1);
vfo->wfLbwSelMin = ImVec2(vfo->wfRectMin.x - 2, vfo->wfRectMin.y); vfo->wfLbwSelMin = ImVec2(vfo->wfRectMin.x - 2, vfo->wfRectMin.y);
vfo->wfLbwSelMax = ImVec2(vfo->wfRectMin.x + 2, vfo->wfRectMax.y); vfo->wfLbwSelMax = ImVec2(vfo->wfRectMin.x + 2, vfo->wfRectMax.y);
vfo->wfRbwSelMin = ImVec2(vfo->wfRectMax.x - 2, vfo->wfRectMin.y); vfo->wfRbwSelMin = ImVec2(vfo->wfRectMax.x - 2, vfo->wfRectMin.y);
@ -1175,16 +1180,16 @@ namespace ImGui {
// Calculate the position of the line // Calculate the position of the line
if (reference == REF_LOWER) { if (reference == REF_LOWER) {
lineMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 9); lineMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(widgetPos.x + 50 + left, widgetPos.y + fftHeight + 9); lineMax = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMax.y - 1);
} }
else if (reference == REF_CENTER) { else if (reference == REF_CENTER) {
lineMin = ImVec2(widgetPos.x + 50 + center, widgetPos.y + 9); lineMin = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(widgetPos.x + 50 + center, widgetPos.y + fftHeight + 9); lineMax = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMax.y - 1);
} }
else if (reference == REF_UPPER) { else if (reference == REF_UPPER) {
lineMin = ImVec2(widgetPos.x + 50 + right, widgetPos.y + 9); lineMin = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(widgetPos.x + 50 + right, widgetPos.y + fftHeight + 9); lineMax = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMax.y - 1);
} }
int _left = left; int _left = left;
@ -1194,22 +1199,23 @@ namespace ImGui {
leftClamped = (left != _left); leftClamped = (left != _left);
rightClamped = (right != _right); rightClamped = (right != _right);
rectMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 10); rectMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y + 1);
rectMax = ImVec2(widgetPos.x + 51 + right, widgetPos.y + fftHeight + 10); rectMax = ImVec2(gui::waterfall.fftAreaMin.x + right + 1, gui::waterfall.fftAreaMax.y);
lbwSelMin = ImVec2(rectMin.x - 2, rectMin.y); float gripSize = 2.0f * style::uiScale;
lbwSelMax = ImVec2(rectMin.x + 2, rectMax.y); lbwSelMin = ImVec2(rectMin.x - gripSize, rectMin.y);
rbwSelMin = ImVec2(rectMax.x - 2, rectMin.y); lbwSelMax = ImVec2(rectMin.x + gripSize, rectMax.y);
rbwSelMax = ImVec2(rectMax.x + 2, rectMax.y); rbwSelMin = ImVec2(rectMax.x - gripSize, rectMin.y);
rbwSelMax = ImVec2(rectMax.x + gripSize, rectMax.y);
notchMin = ImVec2(widgetPos.x + 50 + notch - 2, widgetPos.y + 9); notchMin = ImVec2(gui::waterfall.fftAreaMin.x + notch - gripSize, gui::waterfall.fftAreaMin.y);
notchMax = ImVec2(widgetPos.x + 50 + notch + 2, widgetPos.y + fftHeight + 9); notchMax = ImVec2(gui::waterfall.fftAreaMin.x + notch + gripSize, gui::waterfall.fftAreaMax.y - 1);
} }
void WaterfallVFO::draw(ImGuiWindow* window, bool selected) { void WaterfallVFO::draw(ImGuiWindow* window, bool selected) {
window->DrawList->AddRectFilled(rectMin, rectMax, color); window->DrawList->AddRectFilled(rectMin, rectMax, color);
if (lineVisible) { if (lineVisible) {
window->DrawList->AddLine(lineMin, lineMax, selected ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255)); window->DrawList->AddLine(lineMin, lineMax, selected ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255), style::uiScale);
} }
if (notchVisible) { if (notchVisible) {

View File

@ -236,6 +236,13 @@ namespace ImGui {
_BANDPLAN_POS_COUNT _BANDPLAN_POS_COUNT
}; };
ImVec2 fftAreaMin;
ImVec2 fftAreaMax;
ImVec2 freqAreaMin;
ImVec2 freqAreaMax;
ImVec2 wfMin;
ImVec2 wfMax;
private: private:
void drawWaterfall(); void drawWaterfall();
void drawFFT(); void drawFFT();
@ -260,13 +267,6 @@ namespace ImGui {
ImVec2 lastWidgetPos; ImVec2 lastWidgetPos;
ImVec2 lastWidgetSize; ImVec2 lastWidgetSize;
ImVec2 fftAreaMin;
ImVec2 fftAreaMax;
ImVec2 freqAreaMin;
ImVec2 freqAreaMax;
ImVec2 wfMin;
ImVec2 wfMax;
ImGuiWindow* window; ImGuiWindow* window;
GLuint textureId; GLuint textureId;

View File

@ -4,6 +4,9 @@
ModuleManager::Module_t ModuleManager::loadModule(std::string path) { ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
Module_t mod; Module_t mod;
// On android, the path has to be relative, don't make it absolute
#ifndef __ANDROID__
if (!std::filesystem::exists(path)) { if (!std::filesystem::exists(path)) {
spdlog::error("{0} does not exist", path); spdlog::error("{0} does not exist", path);
mod.handle = NULL; mod.handle = NULL;
@ -14,6 +17,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
mod.handle = NULL; mod.handle = NULL;
return mod; return mod;
} }
#endif
#ifdef _WIN32 #ifdef _WIN32
mod.handle = LoadLibraryA(path.c_str()); mod.handle = LoadLibraryA(path.c_str());
if (mod.handle == NULL) { if (mod.handle == NULL) {

View File

@ -13,6 +13,8 @@ namespace options {
#elif defined(IS_MACOS_BUNDLE) #elif defined(IS_MACOS_BUNDLE)
std::string homedir = getenv("HOME"); std::string homedir = getenv("HOME");
opts.root = homedir + "/Library/Application Support/sdrpp"; opts.root = homedir + "/Library/Application Support/sdrpp";
#elif defined(__ANDROID__)
opts.root = "/storage/self/primary/sdrpp";
#else #else
std::string homedir = getenv("HOME"); std::string homedir = getenv("HOME");
opts.root = homedir + "/.config/sdrpp"; opts.root = homedir + "/.config/sdrpp";

View File

@ -248,6 +248,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
} }
float ypos = ImGui::GetCursorPosY(); float ypos = ImGui::GetCursorPosY();
float sliderOffset = 8.0f * style::uiScale;
if (streams.find(name) == streams.end() || name == "") { if (streams.find(name) == streams.end() || name == "") {
float dummy = 0.0f; float dummy = 0.0f;
@ -256,7 +257,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
ImGui::ImageButton(icons::MUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), btwBorder); ImGui::ImageButton(icons::MUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), btwBorder);
ImGui::PopID(); ImGui::PopID();
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(width - height - 8); ImGui::SetNextItemWidth(width - height - sliderOffset);
ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder);
ImGui::SliderFloat((prefix + name).c_str(), &dummy, 0.0f, 1.0f, ""); ImGui::SliderFloat((prefix + name).c_str(), &dummy, 0.0f, 1.0f, "");
style::endDisabled(); style::endDisabled();
@ -289,7 +290,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(width - height - 8); ImGui::SetNextItemWidth(width - height - sliderOffset);
ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder);
if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "")) { if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "")) {
stream->setVolume(stream->guiVolume); stream->setVolume(stream->guiVolume);

View File

@ -5,6 +5,9 @@
#include <GL/gl.h> #include <GL/gl.h>
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <OpenGL/gl.h> #include <OpenGL/gl.h>
#elif defined(__ANDROID__)
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#else #else
#include <GL/gl.h> #include <GL/gl.h>
#endif #endif

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#define VERSION_STR "1.0.5" #define VERSION_STR "1.0.6"

View File

@ -3,7 +3,7 @@
"country_name": "United Kingdom", "country_name": "United Kingdom",
"country_code": "UK", "country_code": "UK",
"author_name": "John Donkersley", "author_name": "John Donkersley",
"author_url": "--", "author_url": "",
"bands": [ "bands": [
{ {
"name": "Long Wave", "name": "Long Wave",
@ -29,18 +29,42 @@
"start": 1810000, "start": 1810000,
"end": 2000000 "end": 2000000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 2045000,
"end": 2300000
},
{ {
"name": "120m Broadcast", "name": "120m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 2300000, "start": 2300000,
"end": 2495000 "end": 2495000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 2500000,
"end": 2850000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 2850000,
"end": 3155000
},
{ {
"name": "90m Broadcast", "name": "90m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 3200000, "start": 3200000,
"end": 3400000 "end": 3400000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 3400000,
"end": 3500000
},
{ {
"name": "80m Ham Band", "name": "80m Ham Band",
"type": "amateur", "type": "amateur",
@ -53,6 +77,18 @@
"start": 3900000, "start": 3900000,
"end": 4000000 "end": 4000000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 4063000,
"end": 4438000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 4650000,
"end": 4750000
},
{ {
"name": "60m Broadcast", "name": "60m Broadcast",
"type": "broadcast", "type": "broadcast",
@ -65,12 +101,30 @@
"start": 5258500, "start": 5258500,
"end": 5406500 "end": 5406500
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 5450000,
"end": 5730000
},
{ {
"name": "49m Broadcast", "name": "49m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 5900000, "start": 5900000,
"end": 6200000 "end": 6200000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 6200000,
"end": 6525000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 6525000,
"end": 6765000
},
{ {
"name": "40m Ham Band", "name": "40m Ham Band",
"type": "amateur", "type": "amateur",
@ -83,23 +137,59 @@
"start": 7200000, "start": 7200000,
"end": 7450000 "end": 7450000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 8195000,
"end": 8815000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 8815000,
"end": 9040000
},
{ {
"name": "31m Broadcast", "name": "31m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 9400000, "start": 9400000,
"end": 9900000 "end": 9900000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 10005000,
"end": 10100000
},
{ {
"name": "30m Ham Band", "name": "30m Ham Band",
"type": "amateur", "type": "amateur",
"start": 10100000, "start": 10100000,
"end": 10150000 "end": 10150000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 11175000,
"end": 11400000
},
{ {
"name": "25m Broadcast", "name": "25m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 11600000, "start": 11600000,
"end": 12100000 "end": 12230000
},
{
"name": "Maritime",
"type": "marine",
"start": 12230000,
"end": 13200000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 13200000,
"end": 13360000
}, },
{ {
"name": "22m Broadcast", "name": "22m Broadcast",
@ -113,6 +203,18 @@
"start": 14000000, "start": 14000000,
"end": 14350000 "end": 14350000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 15010000,
"end": 15100000
},
{
"name": "Maritime",
"type": "marine",
"start": 16360000,
"end": 17410000
},
{ {
"name": "19m Broadcast", "name": "19m Broadcast",
"type": "broadcast", "type": "broadcast",
@ -125,18 +227,36 @@
"start": 17480000, "start": 17480000,
"end": 17900000 "end": 17900000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 17900000,
"end": 18030000
},
{ {
"name": "17m Ham Band", "name": "17m Ham Band",
"type": "amateur", "type": "amateur",
"start": 18068000, "start": 18068000,
"end": 18168000 "end": 18168000
}, },
{
"name": "Maritime - ship tx",
"type": "marine",
"start": 18780000,
"end": 18900000
},
{ {
"name": "15m Broadcast", "name": "15m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 18900000, "start": 18900000,
"end": 19020000 "end": 19020000
}, },
{
"name": "Maritime - coast tx",
"type": "marine",
"start": 19680000,
"end": 19990000
},
{ {
"name": "15m Ham Band", "name": "15m Ham Band",
"type": "amateur", "type": "amateur",
@ -149,6 +269,18 @@
"start": 21450000, "start": 21450000,
"end": 21850000 "end": 21850000
}, },
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 21870000,
"end": 22000000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 23200000,
"end": 23350000
},
{ {
"name": "12m Ham Band", "name": "12m Ham Band",
"type": "amateur", "type": "amateur",
@ -179,6 +311,18 @@
"start": 28000000, "start": 28000000,
"end": 29700000 "end": 29700000
}, },
{
"name": "Analogue Cordless Phones",
"type": "amateur",
"start": 31037500,
"end": 40112500
},
{
"name": "Low Power Devices",
"type": "amateur",
"start": 49820000,
"end": 49987500
},
{ {
"name": "6m Ham Band", "name": "6m Ham Band",
"type": "amateur", "type": "amateur",
@ -198,7 +342,7 @@
"end": 108000000 "end": 108000000
}, },
{ {
"name": "Air Band VOR/ILS", "name": "Air Band TACAN/ILS",
"type": "aviation", "type": "aviation",
"start": 108000000, "start": 108000000,
"end": 118000000 "end": 118000000
@ -206,7 +350,7 @@
{ {
"name": "Air Band Voice", "name": "Air Band Voice",
"type": "aviation", "type": "aviation",
"start": 118000000, "start": 117975000,
"end": 137000000 "end": 137000000
}, },
{ {
@ -222,11 +366,53 @@
"end": 146000000 "end": 146000000
}, },
{ {
"name": "Marine", "name": "Land/Mountain Rescue",
"type": "PMR",
"start": 147343750,
"end": 147500000
},
{
"name": "Pagers - Flex/POCSAG",
"type": "PMR",
"start": 153025000,
"end": 153500000
},
{
"name": "Land/Mountain Rescue DMR",
"type": "PMR",
"start": 155000000,
"end": 156000000
},
{
"name": "Marine - ship tx",
"type": "marine", "type": "marine",
"start": 156000000, "start": 156000000,
"end": 157850000
},
{
"name": "Short Term Hire",
"type": "PMR",
"start": 158787500,
"end": 159687500
},
{
"name": "Marine - coast tx",
"type": "marine",
"start": 160650000,
"end": 162025000 "end": 162025000
}, },
{
"name": "Short Term Hire",
"type": "PMR",
"start": 163287500,
"end": 164200000
},
{
"name": "Business Radio",
"type": "PMR",
"start": 165000000,
"end": 174000000
},
{ {
"name": "DAB Radio", "name": "DAB Radio",
"type": "broadcast", "type": "broadcast",
@ -239,6 +425,12 @@
"start": 230000000, "start": 230000000,
"end": 400000000 "end": 400000000
}, },
{
"name": "Private Mobile Radio inc trams",
"type": "PMR",
"start": 422000000,
"end": 424000000
},
{ {
"name": "70cm Ham Band", "name": "70cm Ham Band",
"type": "amateur", "type": "amateur",
@ -247,16 +439,52 @@
}, },
{ {
"name": "PMR446", "name": "PMR446",
"type": "amateur", "type": "PMR",
"start": 446000000, "start": 446000000,
"end": 446200000 "end": 446200000
}, },
{
"name": "Outside Broadcast Talkback",
"type": "PMR",
"start": 446200000,
"end": 447512500
},
{
"name": "Private Mobile Radio",
"type": "PMR",
"start": 447600000,
"end": 454000000
},
{
"name": "Mosques",
"type": "amateur",
"start": 454000000,
"end": 455000000
},
{
"name": "Private Mobile Radio",
"type": "PMR",
"start": 455000000,
"end": 467200000
},
{
"name": "Outside Broadcast Talkback",
"type": "PMR",
"start": 467200000,
"end": 468600000
},
{ {
"name": "Digital TV Broadcast", "name": "Digital TV Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 470000000, "start": 470000000,
"end": 790000000 "end": 790000000
}, },
{
"name": "Licence Exempt Short Range",
"type": "amateur",
"start": 862000000,
"end": 875800000
},
{ {
"name": "23cm Ham Band", "name": "23cm Ham Band",
"type": "amateur", "type": "amateur",

View File

@ -24,6 +24,16 @@ if (MSVC)
target_include_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/include/libairspy/") target_include_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/include/libairspy/")
target_link_libraries(airspy_source PRIVATE airspy) target_link_libraries(airspy_source PRIVATE airspy)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/airspyone_host/libairspy/src
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/libairspy/${ANDROID_ABI}/libairspy.so
)
else (MSVC) else (MSVC)
find_package(PkgConfig) find_package(PkgConfig)

View File

@ -10,6 +10,10 @@
#include <gui/smgui.h> #include <gui/smgui.h>
#include <airspy.h> #include <airspy.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{ SDRPP_MOD_INFO{
@ -75,6 +79,7 @@ public:
} }
void refresh() { void refresh() {
#ifndef __ANDROID__
devList.clear(); devList.clear();
devListTxt = ""; devListTxt = "";
@ -88,6 +93,18 @@ public:
devListTxt += buf; devListTxt += buf;
devListTxt += '\0'; devListTxt += '\0';
} }
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::AIRSPY_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "Airspy USB";
devList.push_back(0xDEADBEEF);
devListTxt += fakeName;
devListTxt += '\0';
#endif
} }
void selectFirst() { void selectFirst() {
@ -112,7 +129,11 @@ public:
void selectBySerial(uint64_t serial) { void selectBySerial(uint64_t serial) {
airspy_device* dev; airspy_device* dev;
try { try {
#ifndef __ANDROID__
int err = airspy_open_sn(&dev, serial); int err = airspy_open_sn(&dev, serial);
#else
int err = airspy_open_sn(&dev, devFd);
#endif
if (err != 0) { if (err != 0) {
char buf[1024]; char buf[1024];
sprintf(buf, "%016" PRIX64, serial); sprintf(buf, "%016" PRIX64, serial);
@ -245,7 +266,11 @@ private:
return; return;
} }
#ifndef __ANDROID__
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial); int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = airspy_open_sn(&_this->openDev, _this->devFd);
#endif
if (err != 0) { if (err != 0) {
char buf[1024]; char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial); sprintf(buf, "%016" PRIX64, _this->selectedSerial);
@ -571,6 +596,10 @@ private:
bool lnaAgc = false; bool lnaAgc = false;
bool mixerAgc = false; bool mixerAgc = false;
#ifdef __ANDROID__
int devFd = 0;
#endif
std::vector<uint64_t> devList; std::vector<uint64_t> devList;
std::string devListTxt; std::string devListTxt;
std::vector<uint32_t> sampleRateList; std::vector<uint32_t> sampleRateList;

View File

@ -24,6 +24,16 @@ if (MSVC)
target_include_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/include/libairspyhf/") target_include_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/include/libairspyhf/")
target_link_libraries(airspyhf_source PRIVATE airspyhf) target_link_libraries(airspyhf_source PRIVATE airspyhf)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/airspyhf/libairspyhf/src
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/libairspyhf/${ANDROID_ABI}/libairspyhf.so
)
else (MSVC) else (MSVC)
find_package(PkgConfig) find_package(PkgConfig)

View File

@ -10,6 +10,10 @@
#include <airspyhf.h> #include <airspyhf.h>
#include <gui/widgets/stepped_slider.h> #include <gui/widgets/stepped_slider.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{ SDRPP_MOD_INFO{
@ -79,6 +83,7 @@ public:
devList.clear(); devList.clear();
devListTxt = ""; devListTxt = "";
#ifndef __ANDROID__
uint64_t serials[256]; uint64_t serials[256];
int n = airspyhf_list_devices(serials, 256); int n = airspyhf_list_devices(serials, 256);
@ -89,6 +94,18 @@ public:
devListTxt += buf; devListTxt += buf;
devListTxt += '\0'; devListTxt += '\0';
} }
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::AIRSPYHF_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "Airspy HF+ USB";
devList.push_back(0xDEADBEEF);
devListTxt += fakeName;
devListTxt += '\0';
#endif
} }
void selectFirst() { void selectFirst() {
@ -113,10 +130,14 @@ public:
void selectBySerial(uint64_t serial) { void selectBySerial(uint64_t serial) {
airspyhf_device_t* dev; airspyhf_device_t* dev;
try { try {
int err = airspyhf_open_sn(&dev, selectedSerial); #ifndef __ANDROID__
int err = airspyhf_open_sn(&dev, serial);
#else
int err = airspyhf_open_sn(&dev, devFd);
#endif
if (err != 0) { if (err != 0) {
char buf[1024]; char buf[1024];
sprintf(buf, "%016" PRIX64, selectedSerial); sprintf(buf, "%016" PRIX64, serial);
spdlog::error("Could not open Airspy HF+ {0}", buf); spdlog::error("Could not open Airspy HF+ {0}", buf);
selectedSerial = 0; selectedSerial = 0;
return; return;
@ -124,7 +145,7 @@ public:
} }
catch (std::exception e) { catch (std::exception e) {
char buf[1024]; char buf[1024];
sprintf(buf, "%016" PRIX64, selectedSerial); sprintf(buf, "%016" PRIX64, serial);
spdlog::error("Could not open Airspy HF+ {0}", buf); spdlog::error("Could not open Airspy HF+ {0}", buf);
} }
@ -221,7 +242,11 @@ private:
return; return;
} }
#ifndef __ANDROID__
int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial); int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = airspyhf_open_sn(&_this->openDev, _this->devFd);
#endif
if (err != 0) { if (err != 0) {
char buf[1024]; char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial); sprintf(buf, "%016" PRIX64, _this->selectedSerial);
@ -368,6 +393,10 @@ private:
float atten = 0.0f; float atten = 0.0f;
std::string selectedSerStr = ""; std::string selectedSerStr = "";
#ifdef __ANDROID__
int devFd = 0;
#endif
std::vector<uint64_t> devList; std::vector<uint64_t> devList;
std::string devListTxt; std::string devListTxt;
std::vector<uint32_t> sampleRateList; std::vector<uint32_t> sampleRateList;

View File

@ -22,6 +22,16 @@ if (MSVC)
target_link_directories(hackrf_source PRIVATE "C:/Program Files/PothosSDR/bin/") target_link_directories(hackrf_source PRIVATE "C:/Program Files/PothosSDR/bin/")
target_link_libraries(hackrf_source PRIVATE hackrf) target_link_libraries(hackrf_source PRIVATE hackrf)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/hackrf/host/libhackrf/src
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/libhackrf/${ANDROID_ABI}/libhackrf.so
)
else (MSVC) else (MSVC)
find_package(PkgConfig) find_package(PkgConfig)

View File

@ -5,11 +5,18 @@
#include <core.h> #include <core.h>
#include <gui/style.h> #include <gui/style.h>
#include <config.h> #include <config.h>
#include <libhackrf/hackrf.h>
#include <gui/widgets/stepped_slider.h> #include <gui/widgets/stepped_slider.h>
#include <options.h> #include <options.h>
#include <gui/smgui.h> #include <gui/smgui.h>
#ifndef __ANDROID__
#include <libhackrf/hackrf.h>
#else
#include <android_backend.h>
#include <spdlog/sinks/android_sink.h>
#include <hackrf.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{ SDRPP_MOD_INFO{
@ -127,6 +134,7 @@ public:
devList.clear(); devList.clear();
devListTxt = ""; devListTxt = "";
#ifndef __ANDROID__
uint64_t serials[256]; uint64_t serials[256];
hackrf_device_list_t* _devList = hackrf_device_list(); hackrf_device_list_t* _devList = hackrf_device_list();
@ -137,6 +145,15 @@ public:
} }
hackrf_device_list_free(_devList); hackrf_device_list_free(_devList);
#else
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::HACKRF_VIDPIDS);
if (devFd < 0) { return; }
std::string fakeName = "HackRF USB";
devList.push_back("fake_serial");
devListTxt += fakeName;
devListTxt += '\0';
#endif
} }
void selectFirst() { void selectFirst() {
@ -229,7 +246,11 @@ private:
return; return;
} }
#ifndef __ANDROID__
hackrf_error err = (hackrf_error)hackrf_open_by_serial(_this->selectedSerial.c_str(), &_this->openDev); hackrf_error err = (hackrf_error)hackrf_open_by_serial(_this->selectedSerial.c_str(), &_this->openDev);
#else
hackrf_error err = (hackrf_error)hackrf_open_by_fd(&_this->openDev, _this->devFd);
#endif
if (err != HACKRF_SUCCESS) { if (err != HACKRF_SUCCESS) {
spdlog::error("Could not open HackRF {0}: {1}", _this->selectedSerial, hackrf_error_name(err)); spdlog::error("Could not open HackRF {0}: {1}", _this->selectedSerial, hackrf_error_name(err));
return; return;
@ -383,6 +404,10 @@ private:
float lna = 0; float lna = 0;
float vga = 0; float vga = 0;
#ifdef __ANDROID__
int devFd = -1;
#endif
std::vector<std::string> devList; std::vector<std::string> devList;
std::string devListTxt; std::string devListTxt;
}; };

View File

@ -24,6 +24,17 @@ if (MSVC)
target_link_libraries(plutosdr_source PRIVATE libiio) target_link_libraries(plutosdr_source PRIVATE libiio)
target_link_libraries(plutosdr_source PRIVATE libad9361) target_link_libraries(plutosdr_source PRIVATE libad9361)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libiio
/mnt/android_sdr/libad9361-iio
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libxml2/${ANDROID_ABI}/libxml2.so
/mnt/android_sdr/output/libiio/${ANDROID_ABI}/libiio.so
/mnt/android_sdr/output/libad9361/${ANDROID_ABI}/libad9361.so
)
else (MSVC) else (MSVC)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(plutosdr_source PRIVATE "/Library/Frameworks/iio.framework/Headers") target_include_directories(plutosdr_source PRIVATE "/Library/Frameworks/iio.framework/Headers")

View File

@ -22,6 +22,16 @@ if (MSVC)
target_link_directories(rtl_sdr_source PRIVATE "C:/Program Files/PothosSDR/bin/") target_link_directories(rtl_sdr_source PRIVATE "C:/Program Files/PothosSDR/bin/")
target_link_libraries(rtl_sdr_source PRIVATE rtlsdr) target_link_libraries(rtl_sdr_source PRIVATE rtlsdr)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/librtlsdr/include
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/librtlsdr/${ANDROID_ABI}/librtlsdr.so
)
else (MSVC) else (MSVC)
find_package(PkgConfig) find_package(PkgConfig)

View File

@ -9,6 +9,9 @@
#include <gui/smgui.h> #include <gui/smgui.h>
#include <rtl-sdr.h> #include <rtl-sdr.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -114,6 +117,7 @@ public:
devNames.clear(); devNames.clear();
devListTxt = ""; devListTxt = "";
#ifndef __ANDROID__
devCount = rtlsdr_get_device_count(); devCount = rtlsdr_get_device_count();
char buf[1024]; char buf[1024];
for (int i = 0; i < devCount; i++) { for (int i = 0; i < devCount; i++) {
@ -123,6 +127,20 @@ public:
devListTxt += buf; devListTxt += buf;
devListTxt += '\0'; devListTxt += '\0';
} }
#else
// Check for device connection
devCount = 0;
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::RTL_SDR_VIDPIDS);
if (devFd < 0) { return; }
// Generate fake device info
devCount = 1;
std::string fakeName = "RTL-SDR Dongle USB";
devNames.push_back(fakeName);
devListTxt += fakeName;
devListTxt += '\0';
#endif
} }
void selectFirst() { void selectFirst() {
@ -144,9 +162,15 @@ public:
void selectById(int id) { void selectById(int id) {
selectedDevName = devNames[id]; selectedDevName = devNames[id];
if (rtlsdr_open(&openDev, id) < 0) { #ifndef __ANDROID__
int oret = rtlsdr_open(&openDev, id);
#else
int oret = rtlsdr_open(&openDev, devFd);
#endif
if (oret < 0) {
selectedDevName = ""; selectedDevName = "";
spdlog::error("Could not open RTL-SDR"); spdlog::error("Could not open RTL-SDR: {0}", oret);
return; return;
} }
@ -252,7 +276,13 @@ private:
return; return;
} }
if (rtlsdr_open(&_this->openDev, _this->devId) < 0) { #ifndef __ANDROID__
int oret = rtlsdr_open(&_this->openDev, _this->devId);
#else
int oret = rtlsdr_open(&_this->openDev, _this->devFd);
#endif
if (oret < 0) {
spdlog::error("Could not open RTL-SDR"); spdlog::error("Could not open RTL-SDR");
return; return;
} }
@ -510,6 +540,10 @@ private:
int devCount = 0; int devCount = 0;
std::thread workerThread; std::thread workerThread;
#ifdef __ANDROID__
int devFd = -1;
#endif
int ppm = 0; int ppm = 0;
bool biasT = false; bool biasT = false;