mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	Backend abstraction + added android support + partial high DPI scaling
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -14,4 +14,6 @@ Folder.DotSettings.user | ||||
| CMakeSettings.json | ||||
| poggers_decoder | ||||
| m17_decoder/libcorrect | ||||
| SDR++.app | ||||
| SDR++.app | ||||
| android/deps | ||||
| android/app/assets | ||||
| @@ -7,8 +7,19 @@ else() | ||||
|     set(CMAKE_INSTALL_PREFIX "/usr") | ||||
| 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 | ||||
| option(OPT_BACKEND_GLFW "Use the GLFW backend" ON) | ||||
| option(OPT_BACKEND_ANDROID "Use the Android backend" OFF) | ||||
|  | ||||
| # Compatibility Options | ||||
| 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) | ||||
|  | ||||
| # 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_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF) | ||||
| option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON) | ||||
| @@ -121,6 +133,10 @@ endif (OPT_BUILD_PLUTOSDR_SOURCE) | ||||
|  | ||||
|  | ||||
| # 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) | ||||
| add_subdirectory("sink_modules/audio_sink") | ||||
| endif (OPT_BUILD_AUDIO_SINK) | ||||
|   | ||||
| @@ -21,6 +21,10 @@ endif () | ||||
| if (OPT_BACKEND_GLFW) | ||||
|     file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c") | ||||
| 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_library(sdrpp_core SHARED ${SRC} ${BACKEND_SRC}) | ||||
| @@ -59,6 +63,10 @@ if (OPT_BACKEND_GLFW) | ||||
|         target_link_libraries(sdrpp_core PUBLIC ${GLFW3_LIBRARIES}) | ||||
|     endif() | ||||
| 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 | ||||
| if (USE_INTERNAL_LIBCORRECT) | ||||
| @@ -98,7 +106,24 @@ if (MSVC) | ||||
|     # ZSTD | ||||
|     find_package(zstd CONFIG REQUIRED) | ||||
|     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() | ||||
|     find_package(PkgConfig) | ||||
|     find_package(OpenGL REQUIRED) | ||||
|   | ||||
| @@ -21,4 +21,7 @@ | ||||
| #define KB_KEY_LCTRL        GLFW_KEY_LEFT_CONTROL | ||||
| #define KB_KEY_RCTRL        GLFW_KEY_RIGHT_CONTROL | ||||
| #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 | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| namespace backend { | ||||
|     int init(std::string resDir); | ||||
|     int init(std::string resDir = ""); | ||||
|     void beginFrame(); | ||||
|     void render(bool vsync = true); | ||||
|     void getMouseScreenPos(double& x, double& y); | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| #include <server.h> | ||||
| #include "imgui.h" | ||||
| #include "imgui_impl_glfw.h" | ||||
| #include "imgui_impl_opengl3.h" | ||||
| #include <stdio.h> | ||||
| #include <gui/main_window.h> | ||||
| #include <gui/style.h> | ||||
| @@ -188,6 +186,7 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|     defConfig["theme"] = "Dark"; | ||||
|  | ||||
|     defConfig["modules"] = json::array(); | ||||
|  | ||||
|     defConfig["offsetMode"] = (int)0; // Off | ||||
|     defConfig["offset"] = 0.0; | ||||
|     defConfig["showMenu"] = true; | ||||
| @@ -213,6 +212,9 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
| #elif defined(IS_MACOS_BUNDLE) | ||||
|     defConfig["modulesDirectory"] = "../Plugins"; | ||||
|     defConfig["resourcesDirectory"] = "../Resources"; | ||||
| #elif defined(__ANDROID__) | ||||
|     defConfig["modulesDirectory"] = options::opts.root + "/modules"; | ||||
|     defConfig["resourcesDirectory"] = options::opts.root + "/res"; | ||||
| #else | ||||
|     defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins"; | ||||
|     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.load(defConfig); | ||||
|     core::configManager.enableAutoSave(); | ||||
|  | ||||
|     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 | ||||
|     for (auto const& item : defConfig.items()) { | ||||
|         if (!core::configManager.conf.contains(item.key())) { | ||||
| @@ -274,6 +301,7 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|  | ||||
|     if (!style::loadFonts(resDir)) { return -1; } | ||||
|     thememenu::init(resDir); | ||||
|     LoadingScreen::init(); | ||||
|  | ||||
|     LoadingScreen::show("Loading icons"); | ||||
|     spdlog::info("Loading icons"); | ||||
| @@ -294,6 +322,8 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|     // Run render loop (TODO: CHECK RETURN VALUE) | ||||
|     backend::renderLoop(); | ||||
|  | ||||
|     // On android, none of this shutdown should happen due to the way the UI works | ||||
| #ifndef __ANDROID__ | ||||
|     // Shut down all modules | ||||
|     for (auto& [name, mod] : core::moduleManager.modules) { | ||||
|         mod.end(); | ||||
| @@ -306,6 +336,7 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|  | ||||
|     core::configManager.disableAutoSave(); | ||||
|     core::configManager.save(); | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -8,8 +8,10 @@ | ||||
|  | ||||
| namespace credits { | ||||
|     ImFont* bigFont; | ||||
|     ImVec2 imageSize(128.0f, 128.0f); | ||||
|  | ||||
|     void init() { | ||||
|         imageSize = ImVec2(128.0f * style::uiScale, 128.0f * style::uiScale); | ||||
|     } | ||||
|  | ||||
|     void show() { | ||||
| @@ -25,7 +27,7 @@ namespace credits { | ||||
|         ImGui::TextUnformatted("SDR++          "); | ||||
|         ImGui::PopFont(); | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::Image(icons::LOGO, ImVec2(128, 128)); | ||||
|         ImGui::Image(icons::LOGO, imageSize); | ||||
|         ImGui::Spacing(); | ||||
|         ImGui::Spacing(); | ||||
|         ImGui::Spacing(); | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| #include <gui/dialogs/loading_screen.h> | ||||
| #include <gui/main_window.h> | ||||
| #include <imgui.h> | ||||
| #include "imgui_impl_glfw.h" | ||||
| #include "imgui_impl_opengl3.h" | ||||
| #include <gui/icons.h> | ||||
| #include <gui/style.h> | ||||
| #include <credits.h> | ||||
| @@ -10,6 +8,11 @@ | ||||
| #include <backend.h> | ||||
|  | ||||
| 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) { | ||||
|         backend::beginFrame(); | ||||
| @@ -26,7 +29,7 @@ namespace LoadingScreen { | ||||
|         ImGui::TextUnformatted("SDR++    "); | ||||
|         ImGui::PopFont(); | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::Image(icons::LOGO, ImVec2(128, 128)); | ||||
|         ImGui::Image(icons::LOGO, imageSize); | ||||
|  | ||||
|         ImVec2 origPos = ImGui::GetCursorPos(); | ||||
|         ImGui::SetCursorPosY(origPos.y + 50); | ||||
|   | ||||
| @@ -4,5 +4,6 @@ | ||||
| #include <mutex> | ||||
|  | ||||
| namespace LoadingScreen { | ||||
|     void init(); | ||||
|     void show(std::string msg); | ||||
| }; | ||||
| @@ -1,8 +1,6 @@ | ||||
| #include <gui/main_window.h> | ||||
| #include <gui/gui.h> | ||||
| #include "imgui.h" | ||||
| #include "imgui_impl_glfw.h" | ||||
| #include "imgui_impl_opengl3.h" | ||||
| #include <stdio.h> | ||||
| #include <thread> | ||||
| #include <complex> | ||||
| @@ -126,10 +124,14 @@ void MainWindow::init() { | ||||
|  | ||||
|     // Load additional modules specified through config | ||||
|     for (auto const& path : modules) { | ||||
| #ifndef __ANDROID__ | ||||
|         std::string apath = std::filesystem::absolute(path).string(); | ||||
|         spdlog::info("Loading {0}", apath); | ||||
|         LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string()); | ||||
|         core::moduleManager.loadModule(apath); | ||||
| #else | ||||
|         core::moduleManager.loadModule(path); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     // Create module instances | ||||
| @@ -354,8 +356,9 @@ void MainWindow::draw() { | ||||
|     } | ||||
|  | ||||
|     // To Bar | ||||
|     ImVec2 btnSize(30 * style::uiScale, 30 * style::uiScale); | ||||
|     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; | ||||
|         core::configManager.acquire(); | ||||
|         core::configManager.conf["showMenu"] = showMenu; | ||||
| @@ -369,14 +372,14 @@ void MainWindow::draw() { | ||||
|     if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); } | ||||
|     if (playing) { | ||||
|         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); | ||||
|         } | ||||
|         ImGui::PopID(); | ||||
|     } | ||||
|     else { // TODO: Might need to check if there even is a device | ||||
|         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); | ||||
|         } | ||||
|         ImGui::PopID(); | ||||
| @@ -384,20 +387,21 @@ void MainWindow::draw() { | ||||
|     if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); } | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|     float origY = ImGui::GetCursorPosY(); | ||||
|  | ||||
|     //ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8); | ||||
|     sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248, 30, 5, true); | ||||
|     sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248 * style::uiScale, btnSize.x, 5, true); | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|     ImGui::SetCursorPosY(origY); | ||||
|     gui::freqSelect.draw(); | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|     ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9); | ||||
|     ImGui::SetCursorPosY(origY); | ||||
|     if (tuningMode == tuner::TUNER_MODE_CENTER) { | ||||
|         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; | ||||
|             gui::waterfall.VFOMoveSingleClick = false; | ||||
|             core::configManager.acquire(); | ||||
| @@ -408,7 +412,7 @@ void MainWindow::draw() { | ||||
|     } | ||||
|     else { // TODO: Might need to check if there even is a device | ||||
|         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; | ||||
|             gui::waterfall.VFOMoveSingleClick = true; | ||||
|             tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency); | ||||
| @@ -421,19 +425,20 @@ void MainWindow::draw() { | ||||
|  | ||||
|     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::SetCursorPosY(ImGui::GetCursorPosY() + 5); | ||||
|     ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth + snrOffset)); | ||||
|     ImGui::SetCursorPosY(origY + (5.0f * style::uiScale)); | ||||
|     ImGui::SetNextItemWidth(snrWidth); | ||||
|     ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0); | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|     // Logo button | ||||
|     ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48); | ||||
|     ImGui::SetCursorPosY(10); | ||||
|     if (ImGui::ImageButton(icons::LOGO, ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), 0)) { | ||||
|     ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (48 * style::uiScale)); | ||||
|     ImGui::SetCursorPosY(10 * style::uiScale); | ||||
|     if (ImGui::ImageButton(icons::LOGO, ImVec2(32 * style::uiScale, 32 * style::uiScale), ImVec2(0, 0), ImVec2(1, 1), 0)) { | ||||
|         showCredits = true; | ||||
|     } | ||||
|     if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { | ||||
| @@ -455,7 +460,7 @@ void MainWindow::draw() { | ||||
|             newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250); | ||||
|             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); | ||||
|             if (click) { | ||||
|                 grabbingMenu = true; | ||||
| @@ -479,8 +484,8 @@ void MainWindow::draw() { | ||||
|     if (showMenu) { | ||||
|         ImGui::Columns(3, "WindowColumns", false); | ||||
|         ImGui::SetColumnWidth(0, menuWidth); | ||||
|         ImGui::SetColumnWidth(1, winSize.x - menuWidth - 60); | ||||
|         ImGui::SetColumnWidth(2, 60); | ||||
|         ImGui::SetColumnWidth(1, winSize.x - menuWidth - (60.0f * style::uiScale)); | ||||
|         ImGui::SetColumnWidth(2, 60.0f * style::uiScale); | ||||
|         ImGui::BeginChild("Left Column"); | ||||
|  | ||||
|         if (gui::menu.draw(firstMenuRender)) { | ||||
| @@ -513,7 +518,7 @@ void MainWindow::draw() { | ||||
|             ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency()); | ||||
|             ImGui::Text("Source name: %s", sourceName.c_str()); | ||||
|             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); | ||||
|  | ||||
| @@ -538,9 +543,9 @@ void MainWindow::draw() { | ||||
|     else { | ||||
|         // When hiding the menu bar | ||||
|         ImGui::Columns(3, "WindowColumns", false); | ||||
|         ImGui::SetColumnWidth(0, 8); | ||||
|         ImGui::SetColumnWidth(1, winSize.x - 8 - 60); | ||||
|         ImGui::SetColumnWidth(2, 60); | ||||
|         ImGui::SetColumnWidth(0, 8 * style::uiScale); | ||||
|         ImGui::SetColumnWidth(1, winSize.x - ((8 + 60) * style::uiScale)); | ||||
|         ImGui::SetColumnWidth(2, 60.0f * style::uiScale); | ||||
|     } | ||||
|  | ||||
|     // Right Column | ||||
| @@ -602,8 +607,9 @@ void MainWindow::draw() { | ||||
|  | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0)); | ||||
|     ImGui::TextUnformatted("Zoom"); | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); | ||||
|     if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, 1.0, 0.0, "")) { | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); | ||||
|     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; | ||||
|  | ||||
|         // 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::TextUnformatted("Max"); | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); | ||||
|     if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -160.0f, "")) { | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); | ||||
|     if (ImGui::VSliderFloat("##_8_", wfSliderSize, &fftMax, 0.0, -160.0f, "")) { | ||||
|         fftMax = std::max<float>(fftMax, fftMin + 10); | ||||
|         core::configManager.acquire(); | ||||
|         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::TextUnformatted("Min"); | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); | ||||
|     if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -160.0f, "")) { | ||||
|     ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); | ||||
|     if (ImGui::VSliderFloat("##_9_", wfSliderSize, &fftMin, 0.0, -160.0f, "")) { | ||||
|         fftMin = std::min<float>(fftMax - 10, fftMin); | ||||
|         core::configManager.acquire(); | ||||
|         core::configManager.conf["min"] = fftMin; | ||||
| @@ -707,4 +713,8 @@ void MainWindow::setFFTWindow(int win) { | ||||
|  | ||||
| bool MainWindow::isPlaying() { | ||||
|     return playing; | ||||
| } | ||||
|  | ||||
| void MainWindow::setFirstMenuRender() { | ||||
|     firstMenuRender = true; | ||||
| } | ||||
| @@ -19,6 +19,7 @@ public: | ||||
|     bool sdrIsRunning(); | ||||
|     void setFFTSize(int size); | ||||
|     void setFFTWindow(int win); | ||||
|     void setFirstMenuRender(); | ||||
|  | ||||
|     // TODO: Replace with it's own class | ||||
|     void setVFO(double freq); | ||||
|   | ||||
| @@ -23,8 +23,11 @@ namespace thememenu { | ||||
|             it = std::find(themeNames.begin(), themeNames.end(), "Dark"); | ||||
|             selectedThemeName = "Dark"; | ||||
|         } | ||||
|         gui::themeManager.applyTheme(selectedThemeName); | ||||
|         themeId = std::distance(themeNames.begin(), it); | ||||
|         applyTheme(); | ||||
|  | ||||
|         // Apply scaling | ||||
|         ImGui::GetStyle().ScaleAllSizes(style::uiScale); | ||||
|  | ||||
|         themeNamesTxt = ""; | ||||
|         for (auto name : themeNames) { | ||||
| @@ -33,12 +36,16 @@ namespace thememenu { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|      void applyTheme() { | ||||
|          gui::themeManager.applyTheme(themeNames[themeId]); | ||||
|      } | ||||
|  | ||||
|     void draw(void* ctx) { | ||||
|         float menuWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|         ImGui::LeftLabel("Theme"); | ||||
|         ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|         if (ImGui::Combo("##theme_select_combo", &themeId, themeNamesTxt.c_str())) { | ||||
|             gui::themeManager.applyTheme(themeNames[themeId]); | ||||
|             applyTheme(); | ||||
|             core::configManager.acquire(); | ||||
|             core::configManager.conf["theme"] = themeNames[themeId]; | ||||
|             core::configManager.release(true); | ||||
|   | ||||
| @@ -3,5 +3,6 @@ | ||||
|  | ||||
| namespace thememenu { | ||||
|     void init(std::string resDir); | ||||
|     void applyTheme(); | ||||
|     void draw(void* ctx); | ||||
| } | ||||
| @@ -11,15 +11,21 @@ namespace style { | ||||
|     ImFont* bigFont; | ||||
|     ImFont* hugeFont; | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|     float uiScale = 1.0f; | ||||
| #else | ||||
|     float uiScale = 3.0f; | ||||
| #endif | ||||
|  | ||||
|     bool loadFonts(std::string resDir) { | ||||
|         if (!std::filesystem::is_directory(resDir)) { | ||||
|             spdlog::error("Invalid resource directory: {0}", resDir); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); | ||||
|         bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f); | ||||
|         hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.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 * uiScale); | ||||
|         hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f * uiScale); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|   | ||||
| @@ -7,6 +7,8 @@ namespace style { | ||||
|     extern ImFont* bigFont; | ||||
|     extern ImFont* hugeFont; | ||||
|  | ||||
|     extern float uiScale; | ||||
|  | ||||
|     bool setDefaultStyle(std::string resDir); | ||||
|     bool loadFonts(std::string resDir); | ||||
|     void beginDisabled(); | ||||
|   | ||||
| @@ -42,9 +42,6 @@ void FrequencySelect::onPosChange() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void FrequencySelect::onResize() { | ||||
| } | ||||
|  | ||||
| void FrequencySelect::incrementDigit(int i) { | ||||
|     if (i < 0) { | ||||
|         return; | ||||
| @@ -93,34 +90,27 @@ void FrequencySelect::moveCursorToDigit(int i) { | ||||
| } | ||||
|  | ||||
| void FrequencySelect::draw() { | ||||
|     window = ImGui::GetCurrentWindow(); | ||||
|     auto window = ImGui::GetCurrentWindow(); | ||||
|     widgetPos = ImGui::GetWindowContentRegionMin(); | ||||
|     widgetEndPos = ImGui::GetWindowContentRegionMax(); | ||||
|     ImVec2 cursorPos = ImGui::GetCursorPos(); | ||||
|     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); | ||||
|     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) { | ||||
|         lastWidgetPos = widgetPos; | ||||
|         onPosChange(); | ||||
|     } | ||||
|     if (widgetSize.x != lastWidgetSize.x || widgetSize.y != lastWidgetSize.y) { | ||||
|         lastWidgetSize = widgetSize; | ||||
|         onResize(); | ||||
|     } | ||||
|  | ||||
|     ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f); | ||||
|     ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text); | ||||
|  | ||||
|     ImVec2 digitSz = ImGui::CalcTextSize("0"); | ||||
|     ImVec2 commaSz = ImGui::CalcTextSize("."); | ||||
|      | ||||
|     int digitWidth = digitSz.x; | ||||
|     int commaOffset = 0; | ||||
|     float textOffset = 11.0f * style::uiScale; | ||||
|     bool zeros = true; | ||||
|  | ||||
|     ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y))); | ||||
| @@ -134,7 +124,7 @@ void FrequencySelect::draw() { | ||||
|                                   zeros ? disabledColor : textColor, buf); | ||||
|         if ((i + 1) % 3 == 0 && i < 11) { | ||||
|             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, "."); | ||||
|         } | ||||
|     } | ||||
| @@ -223,9 +213,7 @@ void FrequencySelect::draw() { | ||||
|  | ||||
|     ImGui::PopFont(); | ||||
|  | ||||
|     ImGui::SetCursorPosX(digitBottomMaxs[11].x + 17); | ||||
|  | ||||
|     //ImGui::NewLine(); | ||||
|     ImGui::SetCursorPosX(digitBottomMaxs[11].x + (17.0f * style::uiScale)); | ||||
| } | ||||
|  | ||||
| void FrequencySelect::setFrequency(int64_t freq) { | ||||
|   | ||||
| @@ -26,13 +26,7 @@ private: | ||||
|     void moveCursorToDigit(int i); | ||||
|  | ||||
|     ImVec2 widgetPos; | ||||
|     ImVec2 widgetEndPos; | ||||
|     ImVec2 widgetSize; | ||||
|  | ||||
|     ImVec2 lastWidgetPos; | ||||
|     ImVec2 lastWidgetSize; | ||||
|  | ||||
|     ImGuiWindow* window; | ||||
|  | ||||
|     int digits[12]; | ||||
|     ImVec2 digitBottomMins[12]; | ||||
|   | ||||
| @@ -28,6 +28,7 @@ bool Menu::draw(bool updateStates) { | ||||
|     bool changed = false; | ||||
|     float menuWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|     ImGuiWindow* window = ImGui::GetCurrentWindow(); | ||||
|     ImVec2 checkboxOffset = ImVec2(menuWidth - ImGui::GetTextLineHeight() - (6.0f * style::uiScale), - ImGui::GetTextLineHeight() - (10.0f * style::uiScale)); | ||||
|  | ||||
|     int displayedCount = 0; | ||||
|     int rawId = 0; | ||||
| @@ -46,6 +47,7 @@ bool Menu::draw(bool updateStates) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // Draw dragged menu item | ||||
|         if (displayedCount == insertBefore && !draggedMenuName.empty()) { | ||||
|             if (updateStates) { ImGui::SetNextItemOpen(false); } | ||||
|             ImVec2 posMin = ImGui::GetCursorScreenPos(); | ||||
| @@ -56,14 +58,14 @@ bool Menu::draw(bool updateStates) { | ||||
|             if (items[draggedOpt.name].inst != NULL) { | ||||
|                 window->WorkRect = orignalRect; | ||||
|                 ImVec2 pos = ImGui::GetCursorPos(); | ||||
|                 ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|                 ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|                 ImGui::SetCursorPosX(pos.x + checkboxOffset.x); | ||||
|                 ImGui::SetCursorPosY(pos.y + checkboxOffset.y); | ||||
|                 bool enabled = items[draggedOpt.name].inst->isEnabled(); | ||||
|                 ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled); | ||||
|                 ImGui::SetCursorPos(pos); | ||||
|             } | ||||
|             style::endDisabled(); | ||||
|             window->DrawList->AddRect(posMin, posMax, textColor); | ||||
|             window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale); | ||||
|         } | ||||
|         displayedCount++; | ||||
|  | ||||
| @@ -72,7 +74,7 @@ bool Menu::draw(bool updateStates) { | ||||
|  | ||||
|         ImRect orginalRect = window->WorkRect; | ||||
|         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(); | ||||
| @@ -93,13 +95,14 @@ bool Menu::draw(bool updateStates) { | ||||
|             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 (ImGui::CollapsingHeader((opt.name + "##sdrpp_main_menu").c_str())) { | ||||
|             if (item.inst != NULL) { | ||||
|                 window->WorkRect = orginalRect; | ||||
|                 ImVec2 pos = ImGui::GetCursorPos(); | ||||
|                 ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|                 ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|                 ImGui::SetCursorPosX(pos.x + checkboxOffset.x); | ||||
|                 ImGui::SetCursorPosY(pos.y + checkboxOffset.y); | ||||
|                 bool enabled = item.inst->isEnabled(); | ||||
|                 if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { | ||||
|                     enabled ? item.inst->enable() : item.inst->disable(); | ||||
| @@ -120,8 +123,8 @@ bool Menu::draw(bool updateStates) { | ||||
|         else if (item.inst != NULL) { | ||||
|             window->WorkRect = orginalRect; | ||||
|             ImVec2 pos = ImGui::GetCursorPos(); | ||||
|             ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|             ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|             ImGui::SetCursorPosX(pos.x + checkboxOffset.x); | ||||
|             ImGui::SetCursorPosY(pos.y + checkboxOffset.y); | ||||
|             bool enabled = item.inst->isEnabled(); | ||||
|             if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) { | ||||
|                 enabled ? item.inst->enable() : item.inst->disable(); | ||||
| @@ -168,7 +171,7 @@ bool Menu::draw(bool updateStates) { | ||||
|         insertBefore = -1; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // TODO: Figure out why the hell this is needed | ||||
|     if (insertBefore == displayedCount && !draggedMenuName.empty()) { | ||||
|         if (updateStates) { ImGui::SetNextItemOpen(false); } | ||||
|         ImVec2 posMin = ImGui::GetCursorScreenPos(); | ||||
| @@ -179,14 +182,14 @@ bool Menu::draw(bool updateStates) { | ||||
|         if (items[draggedOpt.name].inst != NULL) { | ||||
|             window->WorkRect = orignalRect; | ||||
|             ImVec2 pos = ImGui::GetCursorPos(); | ||||
|             ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6); | ||||
|             ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight()); | ||||
|             ImGui::SetCursorPosX(pos.x + checkboxOffset.x); | ||||
|             ImGui::SetCursorPosY(pos.y + checkboxOffset.y); | ||||
|             bool enabled = items[draggedOpt.name].inst->isEnabled(); | ||||
|             ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled); | ||||
|             ImGui::SetCursorPos(pos); | ||||
|         } | ||||
|         style::endDisabled(); | ||||
|         window->DrawList->AddRect(posMin, posMax, textColor); | ||||
|         window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale); | ||||
|     } | ||||
|  | ||||
|     if (!draggedMenuName.empty()) { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include <gui/widgets/volume_meter.h> | ||||
| #include <algorithm> | ||||
| #include <gui/style.h> | ||||
|  | ||||
| #ifndef IMGUI_DEFINE_MATH_OPERATORS | ||||
| #define IMGUI_DEFINE_MATH_OPERATORS | ||||
| @@ -27,15 +28,15 @@ namespace ImGui { | ||||
|         float it = size.x / 9; | ||||
|         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->AddLine(min, min + ImVec2(0, 9), text); | ||||
|         window->DrawList->AddLine(min + ImVec2(0, 9), min + ImVec2(size.x + 1, 9), text); | ||||
|         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, (10.0f * style::uiScale) - 1), text, style::uiScale); | ||||
|         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++) { | ||||
|             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); | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include <volk/volk.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/style.h> | ||||
| #include <keybinds.h> | ||||
|  | ||||
| float DEFAULT_COLOR_MAP[][3] = { | ||||
| @@ -99,51 +100,53 @@ namespace ImGui { | ||||
|         ImU32 trace = ImGui::GetColorU32(ImGuiCol_PlotLines); | ||||
|         ImU32 shadow = ImGui::GetColorU32(ImGuiCol_PlotLines, 0.2); | ||||
|         ImU32 text = ImGui::GetColorU32(ImGuiCol_Text); | ||||
|         float textVOffset = 10.0f * style::uiScale; | ||||
|  | ||||
|         // Vertical scale | ||||
|         for (float line = startLine; line > fftMin; line -= vRange) { | ||||
|             float yPos = widgetPos.y + fftHeight + 10 - ((line - fftMin) * scaleFactor); | ||||
|             window->DrawList->AddLine(ImVec2(roundf(widgetPos.x + 50), roundf(yPos)), | ||||
|                                       ImVec2(roundf(widgetPos.x + dataWidth + 50), roundf(yPos)), | ||||
|                                       IM_COL32(50, 50, 50, 255), 1.0); | ||||
|             float yPos = fftAreaMax.y - ((line - fftMin) * scaleFactor); | ||||
|             window->DrawList->AddLine(ImVec2(fftAreaMin.x, roundf(yPos)), | ||||
|                                       ImVec2(fftAreaMax.x, roundf(yPos)), | ||||
|                                       IM_COL32(50, 50, 50, 255), style::uiScale); | ||||
|             sprintf(buf, "%d", (int)line); | ||||
|             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 | ||||
|         double startFreq = ceilf(lowerFreq / range) * range; | ||||
|         double horizScale = (double)dataWidth / viewBandwidth; | ||||
|         float scaleVOfsset = 7 * style::uiScale; | ||||
|         for (double freq = startFreq; freq < upperFreq; freq += range) { | ||||
|             double xPos = widgetPos.x + 50 + ((freq - lowerFreq) * horizScale); | ||||
|             window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + 10), | ||||
|                                       ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10), | ||||
|                                       IM_COL32(50, 50, 50, 255), 1.0); | ||||
|             window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10), | ||||
|                                       ImVec2(roundf(xPos), widgetPos.y + fftHeight + 17), | ||||
|                                       text, 1.0); | ||||
|             double xPos = fftAreaMin.x + ((freq - lowerFreq) * horizScale); | ||||
|             window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMin.y + 1), | ||||
|                                       ImVec2(roundf(xPos), fftAreaMax.y), | ||||
|                                       IM_COL32(50, 50, 50, 255), style::uiScale); | ||||
|             window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMax.y), | ||||
|                                       ImVec2(roundf(xPos), fftAreaMax.y + scaleVOfsset), | ||||
|                                       text, style::uiScale); | ||||
|             printAndScale(freq, 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 | ||||
|         if (latestFFT != NULL && fftLines != 0) { | ||||
|             for (int i = 1; i < dataWidth; i++) { | ||||
|                 double aPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i - 1] - fftMin) * scaleFactor); | ||||
|                 double bPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i] - fftMin) * scaleFactor); | ||||
|                 aPos = std::clamp<double>(aPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10); | ||||
|                 bPos = std::clamp<double>(bPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10); | ||||
|                 window->DrawList->AddLine(ImVec2(widgetPos.x + 49 + i, roundf(aPos)), | ||||
|                                           ImVec2(widgetPos.x + 50 + i, roundf(bPos)), trace, 1.0); | ||||
|                 window->DrawList->AddLine(ImVec2(widgetPos.x + 50 + i, roundf(bPos)), | ||||
|                                           ImVec2(widgetPos.x + 50 + i, widgetPos.y + fftHeight + 10), shadow, 1.0); | ||||
|                 double aPos = fftAreaMax.y - ((latestFFT[i - 1] - fftMin) * scaleFactor); | ||||
|                 double bPos = fftAreaMax.y - ((latestFFT[i] - fftMin) * scaleFactor); | ||||
|                 aPos = std::clamp<double>(aPos, fftAreaMin.y + 1, fftAreaMax.y); | ||||
|                 bPos = std::clamp<double>(bPos, fftAreaMin.y + 1, fftAreaMax.y); | ||||
|                 window->DrawList->AddLine(ImVec2(fftAreaMin.x + i - 1, roundf(aPos)), | ||||
|                                           ImVec2(fftAreaMin.x + i, roundf(bPos)), trace, 1.0); | ||||
|                 window->DrawList->AddLine(ImVec2(fftAreaMin.x + i, roundf(bPos)), | ||||
|                                           ImVec2(fftAreaMin.x + i, fftAreaMax.y), shadow, 1.0); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         FFTRedrawArgs args; | ||||
|         args.min = ImVec2(widgetPos.x + 50, widgetPos.y + 9); | ||||
|         args.max = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10); | ||||
|         args.min = fftAreaMin; | ||||
|         args.max = fftAreaMax; | ||||
|         args.lowFreq = lowerFreq; | ||||
|         args.highFreq = upperFreq; | ||||
|         args.freqToPixelRatio = horizScale; | ||||
| @@ -152,13 +155,13 @@ namespace ImGui { | ||||
|         onFFTRedraw.emit(args); | ||||
|  | ||||
|         // X Axis | ||||
|         window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 10), | ||||
|                                   ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10), | ||||
|                                   text, 1.0); | ||||
|         window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMax.y), | ||||
|                                   ImVec2(fftAreaMax.x, fftAreaMax.y), | ||||
|                                   text, style::uiScale); | ||||
|         // Y Axis | ||||
|         window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + 9), | ||||
|                                   ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 9), | ||||
|                                   text, 1.0); | ||||
|         window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMin.y), | ||||
|                                   ImVec2(fftAreaMin.x, fftAreaMax.y - 1), | ||||
|                                   text, style::uiScale); | ||||
|     } | ||||
|  | ||||
|     void WaterFall::drawWaterfall() { | ||||
| @@ -173,7 +176,7 @@ namespace ImGui { | ||||
|             for (auto const& [name, vfo] : vfos) { | ||||
|                 window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, vfo->color); | ||||
|                 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, | ||||
|                                                   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); | ||||
|         mouseInFFT = IS_IN_AREA(dragOrigin, fftAreaMin, fftAreaMax); | ||||
|         mouseInWaterfall = IS_IN_AREA(dragOrigin, wfMin, wfMax); | ||||
| @@ -304,7 +307,7 @@ namespace ImGui { | ||||
|             newFFTAreaHeight = mousePos.y - widgetPos.y; | ||||
|             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::GetColorU32(ImGuiCol_SeparatorActive)); | ||||
|                                                     ImGui::GetColorU32(ImGuiCol_SeparatorActive), style::uiScale); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -417,7 +420,7 @@ namespace ImGui { | ||||
|         // 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 (selVfo != NULL) { | ||||
|                 int refCenter = mousePos.x - (widgetPos.x + 50); | ||||
|                 int refCenter = mousePos.x - fftAreaMin.x; | ||||
|                 if (refCenter >= 0 && refCenter < dataWidth) { | ||||
|                     double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset; | ||||
|                     off += centerFreq; | ||||
| @@ -599,10 +602,10 @@ namespace ImGui { | ||||
|         float bpBottom; | ||||
|  | ||||
|         if (bandPlanPos == BANDPLAN_POS_BOTTOM) { | ||||
|             bpBottom = widgetPos.y + fftHeight + 10; | ||||
|             bpBottom = fftAreaMax.y; | ||||
|         } | ||||
|         else { | ||||
|             bpBottom = widgetPos.y + height + 10; | ||||
|             bpBottom = fftAreaMin.y + height + 1; | ||||
|         } | ||||
|  | ||||
|  | ||||
| @@ -620,9 +623,9 @@ namespace ImGui { | ||||
|             start = std::clamp<double>(start, lowerFreq, upperFreq); | ||||
|             end = std::clamp<double>(end, lowerFreq, upperFreq); | ||||
|             center = (start + end) / 2.0; | ||||
|             aPos = widgetPos.x + 50 + ((start - lowerFreq) * horizScale); | ||||
|             bPos = widgetPos.x + 50 + ((end - lowerFreq) * horizScale); | ||||
|             cPos = widgetPos.x + 50 + ((center - lowerFreq) * horizScale); | ||||
|             aPos = fftAreaMin.x + ((start - lowerFreq) * horizScale); | ||||
|             bPos = fftAreaMin.x + ((end - lowerFreq) * horizScale); | ||||
|             cPos = fftAreaMin.x + ((center - lowerFreq) * horizScale); | ||||
|             width = bPos - aPos; | ||||
|             txtSz = ImGui::CalcTextSize(bandplan->bands[i].name.c_str()); | ||||
|             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); | ||||
|                 colorTrans = IM_COL32(255, 255, 255, 100); | ||||
|             } | ||||
|             if (aPos <= widgetPos.x + 50) { | ||||
|                 aPos = widgetPos.x + 51; | ||||
|             if (aPos <= fftAreaMin.x) { | ||||
|                 aPos = fftAreaMin.x + 1; | ||||
|             } | ||||
|             if (bPos <= widgetPos.x + 50) { | ||||
|                 bPos = widgetPos.x + 51; | ||||
|             if (bPos <= fftAreaMin.x) { | ||||
|                 bPos = fftAreaMin.x + 1; | ||||
|             } | ||||
|             if (width >= 1.0) { | ||||
|                 window->DrawList->AddRectFilled(ImVec2(roundf(aPos), bpBottom - height), | ||||
|                                                 ImVec2(roundf(bPos), bpBottom), colorTrans); | ||||
|                 if (startVis) { | ||||
|                     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) { | ||||
|                     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) { | ||||
| @@ -679,15 +682,15 @@ namespace ImGui { | ||||
|         int lastWaterfallHeight = waterfallHeight; | ||||
|  | ||||
|         if (waterfallVisible) { | ||||
|             FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - 50); | ||||
|             FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - (50.0f * style::uiScale)); | ||||
|             newFFTAreaHeight = FFTAreaHeight; | ||||
|             fftHeight = FFTAreaHeight - 50; | ||||
|             waterfallHeight = widgetSize.y - fftHeight - 52; | ||||
|             fftHeight = FFTAreaHeight - (50.0f * style::uiScale); | ||||
|             waterfallHeight = widgetSize.y - fftHeight - (50.0f * style::uiScale) - 2; | ||||
|         } | ||||
|         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) { | ||||
|             // Raw FFT resize | ||||
| @@ -724,12 +727,14 @@ namespace ImGui { | ||||
|             latestFFT[i] = -1000.0; // Hide everything | ||||
|         } | ||||
|  | ||||
|         fftAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + 9); | ||||
|         fftAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10); | ||||
|         freqAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 11); | ||||
|         freqAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 50); | ||||
|         wfMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 51); | ||||
|         wfMax = ImVec2(widgetPos.x + 50 + dataWidth, widgetPos.y + fftHeight + 51 + waterfallHeight); | ||||
|         fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale)); | ||||
|         fftAreaMax = ImVec2(fftAreaMin.x + dataWidth, fftAreaMin.y + fftHeight + 1); | ||||
|  | ||||
|         freqAreaMin = ImVec2(fftAreaMin.x, fftAreaMax.y + 1); | ||||
|         freqAreaMax = ImVec2(fftAreaMax.x, fftAreaMax.y + (40.0f * style::uiScale)); | ||||
|  | ||||
|         wfMin = ImVec2(fftAreaMin.x, freqAreaMax.y + 1); | ||||
|         wfMax = ImVec2(fftAreaMin.x + dataWidth, wfMin.y + waterfallHeight); | ||||
|  | ||||
|         maxHSteps = dataWidth / (ImGui::CalcTextSize("000.000").x + 10); | ||||
|         maxVSteps = fftHeight / (ImGui::CalcTextSize("000.000").y); | ||||
| @@ -769,8 +774,8 @@ namespace ImGui { | ||||
|         //window->DrawList->AddRectFilled(widgetPos, widgetEndPos, IM_COL32( 0, 0, 0, 255 )); | ||||
|         ImU32 bg = ImGui::ColorConvertFloat4ToU32(gui::themeManager.waterfallBg); | ||||
|         window->DrawList->AddRectFilled(widgetPos, widgetEndPos, bg); | ||||
|         window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255)); | ||||
|         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->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255), 0.0, 0, style::uiScale); | ||||
|         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) { | ||||
|             inputHandled = false; | ||||
| @@ -1041,8 +1046,8 @@ namespace ImGui { | ||||
|             vfo->updateDrawingVars(viewBandwidth, dataWidth, viewOffset, widgetPos, fftHeight); | ||||
|             vfo->wfRectMin = ImVec2(vfo->rectMin.x, wfMin.y); | ||||
|             vfo->wfRectMax = ImVec2(vfo->rectMax.x, wfMax.y); | ||||
|             vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y); | ||||
|             vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y); | ||||
|             vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y - 1); | ||||
|             vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y - 1); | ||||
|             vfo->wfLbwSelMin = ImVec2(vfo->wfRectMin.x - 2, vfo->wfRectMin.y); | ||||
|             vfo->wfLbwSelMax = ImVec2(vfo->wfRectMin.x + 2, vfo->wfRectMax.y); | ||||
|             vfo->wfRbwSelMin = ImVec2(vfo->wfRectMax.x - 2, vfo->wfRectMin.y); | ||||
| @@ -1175,16 +1180,16 @@ namespace ImGui { | ||||
|  | ||||
|         // Calculate the position of the line | ||||
|         if (reference == REF_LOWER) { | ||||
|             lineMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 9); | ||||
|             lineMax = ImVec2(widgetPos.x + 50 + left, widgetPos.y + fftHeight + 9); | ||||
|             lineMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y); | ||||
|             lineMax = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMax.y - 1); | ||||
|         } | ||||
|         else if (reference == REF_CENTER) { | ||||
|             lineMin = ImVec2(widgetPos.x + 50 + center, widgetPos.y + 9); | ||||
|             lineMax = ImVec2(widgetPos.x + 50 + center, widgetPos.y + fftHeight + 9); | ||||
|             lineMin = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMin.y); | ||||
|             lineMax = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMax.y - 1); | ||||
|         } | ||||
|         else if (reference == REF_UPPER) { | ||||
|             lineMin = ImVec2(widgetPos.x + 50 + right, widgetPos.y + 9); | ||||
|             lineMax = ImVec2(widgetPos.x + 50 + right, widgetPos.y + fftHeight + 9); | ||||
|             lineMin = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMin.y); | ||||
|             lineMax = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMax.y - 1); | ||||
|         } | ||||
|  | ||||
|         int _left = left; | ||||
| @@ -1194,22 +1199,23 @@ namespace ImGui { | ||||
|         leftClamped = (left != _left); | ||||
|         rightClamped = (right != _right); | ||||
|  | ||||
|         rectMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 10); | ||||
|         rectMax = ImVec2(widgetPos.x + 51 + right, widgetPos.y + fftHeight + 10); | ||||
|         rectMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y + 1); | ||||
|         rectMax = ImVec2(gui::waterfall.fftAreaMin.x + right + 1, gui::waterfall.fftAreaMax.y); | ||||
|  | ||||
|         lbwSelMin = ImVec2(rectMin.x - 2, rectMin.y); | ||||
|         lbwSelMax = ImVec2(rectMin.x + 2, rectMax.y); | ||||
|         rbwSelMin = ImVec2(rectMax.x - 2, rectMin.y); | ||||
|         rbwSelMax = ImVec2(rectMax.x + 2, rectMax.y); | ||||
|         float gripSize = 2.0f * style::uiScale; | ||||
|         lbwSelMin = ImVec2(rectMin.x - gripSize, rectMin.y); | ||||
|         lbwSelMax = ImVec2(rectMin.x + gripSize, 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); | ||||
|         notchMax = ImVec2(widgetPos.x + 50 + notch + 2, widgetPos.y + fftHeight + 9); | ||||
|         notchMin = ImVec2(gui::waterfall.fftAreaMin.x + notch - gripSize, gui::waterfall.fftAreaMin.y); | ||||
|         notchMax = ImVec2(gui::waterfall.fftAreaMin.x + notch + gripSize, gui::waterfall.fftAreaMax.y - 1); | ||||
|     } | ||||
|  | ||||
|     void WaterfallVFO::draw(ImGuiWindow* window, bool selected) { | ||||
|         window->DrawList->AddRectFilled(rectMin, rectMax, color); | ||||
|         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) { | ||||
|   | ||||
| @@ -236,6 +236,13 @@ namespace ImGui { | ||||
|             _BANDPLAN_POS_COUNT | ||||
|         }; | ||||
|  | ||||
|         ImVec2 fftAreaMin; | ||||
|         ImVec2 fftAreaMax; | ||||
|         ImVec2 freqAreaMin; | ||||
|         ImVec2 freqAreaMax; | ||||
|         ImVec2 wfMin; | ||||
|         ImVec2 wfMax; | ||||
|  | ||||
|     private: | ||||
|         void drawWaterfall(); | ||||
|         void drawFFT(); | ||||
| @@ -260,13 +267,6 @@ namespace ImGui { | ||||
|         ImVec2 lastWidgetPos; | ||||
|         ImVec2 lastWidgetSize; | ||||
|  | ||||
|         ImVec2 fftAreaMin; | ||||
|         ImVec2 fftAreaMax; | ||||
|         ImVec2 freqAreaMin; | ||||
|         ImVec2 freqAreaMax; | ||||
|         ImVec2 wfMin; | ||||
|         ImVec2 wfMax; | ||||
|  | ||||
|         ImGuiWindow* window; | ||||
|  | ||||
|         GLuint textureId; | ||||
|   | ||||
| @@ -4,6 +4,9 @@ | ||||
|  | ||||
| ModuleManager::Module_t ModuleManager::loadModule(std::string path) { | ||||
|     Module_t mod; | ||||
|  | ||||
|     // On android, the path has to be relative, don't make it absolute | ||||
| #ifndef __ANDROID__ | ||||
|     if (!std::filesystem::exists(path)) { | ||||
|         spdlog::error("{0} does not exist", path); | ||||
|         mod.handle = NULL; | ||||
| @@ -14,6 +17,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) { | ||||
|         mod.handle = NULL; | ||||
|         return mod; | ||||
|     } | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|     mod.handle = LoadLibraryA(path.c_str()); | ||||
|     if (mod.handle == NULL) { | ||||
|   | ||||
| @@ -13,6 +13,8 @@ namespace options { | ||||
| #elif defined(IS_MACOS_BUNDLE) | ||||
|         std::string homedir = getenv("HOME"); | ||||
|         opts.root = homedir + "/Library/Application Support/sdrpp"; | ||||
| #elif defined(__ANDROID__) | ||||
|         opts.root = "/storage/self/primary/sdrpp"; | ||||
| #else | ||||
|         std::string homedir = getenv("HOME"); | ||||
|         opts.root = homedir + "/.config/sdrpp"; | ||||
|   | ||||
| @@ -248,6 +248,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w | ||||
|     } | ||||
|  | ||||
|     float ypos = ImGui::GetCursorPosY(); | ||||
|     float sliderOffset = 8.0f * style::uiScale; | ||||
|  | ||||
|     if (streams.find(name) == streams.end() || name == "") { | ||||
|         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::PopID(); | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::SetNextItemWidth(width - height - 8); | ||||
|         ImGui::SetNextItemWidth(width - height - sliderOffset); | ||||
|         ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); | ||||
|         ImGui::SliderFloat((prefix + name).c_str(), &dummy, 0.0f, 1.0f, ""); | ||||
|         style::endDisabled(); | ||||
| @@ -289,7 +290,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|     ImGui::SetNextItemWidth(width - height - 8); | ||||
|     ImGui::SetNextItemWidth(width - height - sliderOffset); | ||||
|     ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); | ||||
|     if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "")) { | ||||
|         stream->setVolume(stream->guiVolume); | ||||
|   | ||||
| @@ -5,6 +5,9 @@ | ||||
| #include <GL/gl.h> | ||||
| #elif defined(__APPLE__) | ||||
| #include <OpenGL/gl.h> | ||||
| #elif defined(__ANDROID__) | ||||
| #include <EGL/egl.h> | ||||
| #include <GLES3/gl3.h> | ||||
| #else | ||||
| #include <GL/gl.h> | ||||
| #endif | ||||
| @@ -1,3 +1,3 @@ | ||||
| #pragma once | ||||
|  | ||||
| #define VERSION_STR "1.0.5" | ||||
| #define VERSION_STR "1.0.6" | ||||
| @@ -3,7 +3,7 @@ | ||||
|     "country_name": "United Kingdom", | ||||
|     "country_code": "UK", | ||||
|     "author_name": "John Donkersley", | ||||
|     "author_url": "--", | ||||
|     "author_url": "", | ||||
|     "bands": [ | ||||
|         { | ||||
|             "name": "Long Wave", | ||||
| @@ -29,18 +29,42 @@ | ||||
|             "start": 1810000, | ||||
|             "end": 2000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 2045000, | ||||
|             "end": 2300000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "120m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 2300000, | ||||
|             "end": 2495000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 2500000, | ||||
|             "end": 2850000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 2850000, | ||||
|             "end": 3155000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "90m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 3200000, | ||||
|             "end": 3400000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 3400000, | ||||
|             "end": 3500000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "80m Ham Band", | ||||
|             "type": "amateur", | ||||
| @@ -53,6 +77,18 @@ | ||||
|             "start": 3900000, | ||||
|             "end": 4000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 4063000, | ||||
|             "end": 4438000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 4650000, | ||||
|             "end": 4750000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "60m Broadcast", | ||||
|             "type": "broadcast", | ||||
| @@ -65,12 +101,30 @@ | ||||
|             "start": 5258500, | ||||
|             "end": 5406500 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 5450000, | ||||
|             "end": 5730000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "49m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 5900000, | ||||
|             "end": 6200000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 6200000, | ||||
|             "end": 6525000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 6525000, | ||||
|             "end": 6765000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "40m Ham Band", | ||||
|             "type": "amateur", | ||||
| @@ -83,23 +137,59 @@ | ||||
|             "start": 7200000, | ||||
|             "end": 7450000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 8195000, | ||||
|             "end": 8815000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 8815000, | ||||
|             "end": 9040000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "31m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 9400000, | ||||
|             "end": 9900000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 10005000, | ||||
|             "end": 10100000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "30m Ham Band", | ||||
|             "type": "amateur", | ||||
|             "start": 10100000, | ||||
|             "end": 10150000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 11175000, | ||||
|             "end": 11400000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "25m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "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", | ||||
| @@ -113,6 +203,18 @@ | ||||
|             "start": 14000000, | ||||
|             "end": 14350000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 15010000, | ||||
|             "end": 15100000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 16360000, | ||||
|             "end": 17410000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "19m Broadcast", | ||||
|             "type": "broadcast", | ||||
| @@ -125,18 +227,36 @@ | ||||
|             "start": 17480000, | ||||
|             "end": 17900000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 17900000, | ||||
|             "end": 18030000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "17m Ham Band", | ||||
|             "type": "amateur", | ||||
|             "start": 18068000, | ||||
|             "end": 18168000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime - ship tx", | ||||
|             "type": "marine", | ||||
|             "start": 18780000, | ||||
|             "end": 18900000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "15m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 18900000, | ||||
|             "end": 19020000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime - coast tx", | ||||
|             "type": "marine", | ||||
|             "start": 19680000, | ||||
|             "end": 19990000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "15m Ham Band", | ||||
|             "type": "amateur", | ||||
| @@ -149,6 +269,18 @@ | ||||
|             "start": 21450000, | ||||
|             "end": 21850000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 21870000, | ||||
|             "end": 22000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Aeronautical Mobile", | ||||
|             "type": "aviation", | ||||
|             "start": 23200000, | ||||
|             "end": 23350000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "12m Ham Band", | ||||
|             "type": "amateur", | ||||
| @@ -179,6 +311,18 @@ | ||||
|             "start": 28000000, | ||||
|             "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", | ||||
|             "type": "amateur", | ||||
| @@ -198,7 +342,7 @@ | ||||
|             "end": 108000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Air Band VOR/ILS", | ||||
|             "name": "Air Band TACAN/ILS", | ||||
|             "type": "aviation", | ||||
|             "start": 108000000, | ||||
|             "end": 118000000 | ||||
| @@ -206,7 +350,7 @@ | ||||
|         { | ||||
|             "name": "Air Band Voice", | ||||
|             "type": "aviation", | ||||
|             "start": 118000000, | ||||
|             "start": 117975000, | ||||
|             "end": 137000000 | ||||
|         }, | ||||
|         { | ||||
| @@ -222,11 +366,53 @@ | ||||
|             "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", | ||||
|             "start": 156000000, | ||||
|             "end": 157850000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Short Term Hire", | ||||
|             "type": "PMR", | ||||
|             "start": 158787500, | ||||
|             "end": 159687500 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Marine - coast tx", | ||||
|             "type": "marine", | ||||
|             "start": 160650000, | ||||
|             "end": 162025000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Short Term Hire", | ||||
|             "type": "PMR", | ||||
|             "start": 163287500, | ||||
|             "end": 164200000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Business Radio", | ||||
|             "type": "PMR", | ||||
|             "start": 165000000, | ||||
|             "end": 174000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "DAB Radio", | ||||
|             "type": "broadcast", | ||||
| @@ -239,6 +425,12 @@ | ||||
|             "start": 230000000, | ||||
|             "end": 400000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Private Mobile Radio inc trams", | ||||
|             "type": "PMR", | ||||
|             "start": 422000000, | ||||
|             "end": 424000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "70cm Ham Band", | ||||
|             "type": "amateur", | ||||
| @@ -247,16 +439,52 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "PMR446", | ||||
|             "type": "amateur", | ||||
|             "type": "PMR", | ||||
|             "start": 446000000, | ||||
|             "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", | ||||
|             "type": "broadcast", | ||||
|             "start": 470000000, | ||||
|             "end": 790000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Licence Exempt Short Range", | ||||
|             "type": "amateur", | ||||
|             "start": 862000000, | ||||
|             "end": 875800000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "23cm Ham Band", | ||||
|             "type": "amateur", | ||||
|   | ||||
| @@ -24,6 +24,16 @@ if (MSVC) | ||||
|     target_include_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/include/libairspy/") | ||||
|  | ||||
|     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) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,10 @@ | ||||
| #include <gui/smgui.h> | ||||
| #include <airspy.h> | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
| #include <android_backend.h> | ||||
| #endif | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
| @@ -75,6 +79,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     void refresh() { | ||||
| #ifndef __ANDROID__ | ||||
|         devList.clear(); | ||||
|         devListTxt = ""; | ||||
|  | ||||
| @@ -88,6 +93,18 @@ public: | ||||
|             devListTxt += buf; | ||||
|             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() { | ||||
| @@ -112,7 +129,11 @@ public: | ||||
|     void selectBySerial(uint64_t serial) { | ||||
|         airspy_device* dev; | ||||
|         try { | ||||
| #ifndef __ANDROID__ | ||||
|             int err = airspy_open_sn(&dev, serial); | ||||
| #else | ||||
|             int err = airspy_open_sn(&dev, devFd); | ||||
| #endif | ||||
|             if (err != 0) { | ||||
|                 char buf[1024]; | ||||
|                 sprintf(buf, "%016" PRIX64, serial); | ||||
| @@ -245,7 +266,11 @@ private: | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         int err = airspy_open_sn(&_this->openDev, _this->selectedSerial); | ||||
| #else | ||||
|         int err = airspy_open_sn(&_this->openDev, _this->devFd); | ||||
| #endif | ||||
|         if (err != 0) { | ||||
|             char buf[1024]; | ||||
|             sprintf(buf, "%016" PRIX64, _this->selectedSerial); | ||||
| @@ -571,6 +596,10 @@ private: | ||||
|     bool lnaAgc = false; | ||||
|     bool mixerAgc = false; | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
|     int devFd = 0; | ||||
| #endif | ||||
|  | ||||
|     std::vector<uint64_t> devList; | ||||
|     std::string devListTxt; | ||||
|     std::vector<uint32_t> sampleRateList; | ||||
|   | ||||
| @@ -24,6 +24,16 @@ if (MSVC) | ||||
|     target_include_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/include/libairspyhf/") | ||||
|  | ||||
|     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) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,10 @@ | ||||
| #include <airspyhf.h> | ||||
| #include <gui/widgets/stepped_slider.h> | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
| #include <android_backend.h> | ||||
| #endif | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
| @@ -79,6 +83,7 @@ public: | ||||
|         devList.clear(); | ||||
|         devListTxt = ""; | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         uint64_t serials[256]; | ||||
|         int n = airspyhf_list_devices(serials, 256); | ||||
|  | ||||
| @@ -89,6 +94,18 @@ public: | ||||
|             devListTxt += buf; | ||||
|             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() { | ||||
| @@ -113,10 +130,14 @@ public: | ||||
|     void selectBySerial(uint64_t serial) { | ||||
|         airspyhf_device_t* dev; | ||||
|         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) { | ||||
|                 char buf[1024]; | ||||
|                 sprintf(buf, "%016" PRIX64, selectedSerial); | ||||
|                 sprintf(buf, "%016" PRIX64, serial); | ||||
|                 spdlog::error("Could not open Airspy HF+ {0}", buf); | ||||
|                 selectedSerial = 0; | ||||
|                 return; | ||||
| @@ -124,7 +145,7 @@ public: | ||||
|         } | ||||
|         catch (std::exception e) { | ||||
|             char buf[1024]; | ||||
|             sprintf(buf, "%016" PRIX64, selectedSerial); | ||||
|             sprintf(buf, "%016" PRIX64, serial); | ||||
|             spdlog::error("Could not open Airspy HF+ {0}", buf); | ||||
|         } | ||||
|  | ||||
| @@ -221,7 +242,11 @@ private: | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial); | ||||
| #ifndef __ANDROID__ | ||||
|             int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial); | ||||
| #else | ||||
|             int err = airspyhf_open_sn(&_this->openDev, _this->devFd); | ||||
| #endif | ||||
|         if (err != 0) { | ||||
|             char buf[1024]; | ||||
|             sprintf(buf, "%016" PRIX64, _this->selectedSerial); | ||||
| @@ -368,6 +393,10 @@ private: | ||||
|     float atten = 0.0f; | ||||
|     std::string selectedSerStr = ""; | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
|     int devFd = 0; | ||||
| #endif | ||||
|  | ||||
|     std::vector<uint64_t> devList; | ||||
|     std::string devListTxt; | ||||
|     std::vector<uint32_t> sampleRateList; | ||||
|   | ||||
| @@ -22,6 +22,16 @@ if (MSVC) | ||||
|     target_link_directories(hackrf_source PRIVATE "C:/Program Files/PothosSDR/bin/") | ||||
|  | ||||
|     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) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|   | ||||
| @@ -5,11 +5,18 @@ | ||||
| #include <core.h> | ||||
| #include <gui/style.h> | ||||
| #include <config.h> | ||||
| #include <libhackrf/hackrf.h> | ||||
| #include <gui/widgets/stepped_slider.h> | ||||
| #include <options.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()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
| @@ -127,6 +134,7 @@ public: | ||||
|         devList.clear(); | ||||
|         devListTxt = ""; | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         uint64_t serials[256]; | ||||
|         hackrf_device_list_t* _devList = hackrf_device_list(); | ||||
|  | ||||
| @@ -137,6 +145,15 @@ public: | ||||
|         } | ||||
|  | ||||
|         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() { | ||||
| @@ -229,7 +246,11 @@ private: | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         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) { | ||||
|             spdlog::error("Could not open HackRF {0}: {1}", _this->selectedSerial, hackrf_error_name(err)); | ||||
|             return; | ||||
| @@ -383,6 +404,10 @@ private: | ||||
|     float lna = 0; | ||||
|     float vga = 0; | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
|     int devFd = -1; | ||||
| #endif | ||||
|  | ||||
|     std::vector<std::string> devList; | ||||
|     std::string devListTxt; | ||||
| }; | ||||
|   | ||||
| @@ -24,6 +24,17 @@ if (MSVC) | ||||
|  | ||||
|     target_link_libraries(plutosdr_source PRIVATE libiio) | ||||
|     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) | ||||
|     if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") | ||||
|         target_include_directories(plutosdr_source PRIVATE "/Library/Frameworks/iio.framework/Headers") | ||||
|   | ||||
| @@ -22,6 +22,16 @@ if (MSVC) | ||||
|     target_link_directories(rtl_sdr_source PRIVATE "C:/Program Files/PothosSDR/bin/") | ||||
|  | ||||
|     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) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,9 @@ | ||||
| #include <gui/smgui.h> | ||||
| #include <rtl-sdr.h> | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
| #include <android_backend.h> | ||||
| #endif | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| @@ -114,6 +117,7 @@ public: | ||||
|         devNames.clear(); | ||||
|         devListTxt = ""; | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         devCount = rtlsdr_get_device_count(); | ||||
|         char buf[1024]; | ||||
|         for (int i = 0; i < devCount; i++) { | ||||
| @@ -123,6 +127,20 @@ public: | ||||
|             devListTxt += buf; | ||||
|             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() { | ||||
| @@ -144,9 +162,15 @@ public: | ||||
|     void selectById(int 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 = ""; | ||||
|             spdlog::error("Could not open RTL-SDR"); | ||||
|             spdlog::error("Could not open RTL-SDR: {0}", oret); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -252,7 +276,13 @@ private: | ||||
|             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"); | ||||
|             return; | ||||
|         } | ||||
| @@ -510,6 +540,10 @@ private: | ||||
|     int devCount = 0; | ||||
|     std::thread workerThread; | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
|     int devFd = -1; | ||||
| #endif | ||||
|  | ||||
|     int ppm = 0; | ||||
|  | ||||
|     bool biasT = false; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user